Просмотр исходного кода

refactor access token handling to ensure required scopes are validated and improve scope extraction logic

reiji-h 1 год назад
Родитель
Сommit
24630485b1

+ 4 - 2
apps/app/src/server/middlewares/access-token-parser/index.ts

@@ -6,11 +6,13 @@ import { parserForAccessToken } from './access-token';
 import { parserForApiToken } from './api-token';
 import type { AccessTokenParserReq } from './interfaces';
 
-export const accessTokenParser = (scopes: Scope[]) => {
+export const accessTokenParser = (scopes?: Scope[]) => {
   return async(req: AccessTokenParserReq, res: Response, next: NextFunction): Promise<void> => {
     // TODO: comply HTTP header of RFC6750 / Authorization: Bearer
 
-    parserForAccessToken(scopes)(req, res, next);
+    if (scopes != null) {
+      parserForAccessToken(scopes)(req, res, next);
+    }
     parserForApiToken(req, res, next);
 
     return next();

+ 7 - 10
apps/app/src/server/models/access-token.ts

@@ -44,7 +44,7 @@ export interface IAccessTokenModel extends Model<IAccessTokenDocument> {
   deleteTokenById: (tokenId: Types.ObjectId | string) => Promise<void>
   deleteAllTokensByUserId: (userId: Types.ObjectId | string) => Promise<void>
   deleteExpiredToken: () => Promise<void>
-  findUserIdByToken: (token: string, requiredScopes?: Scope[]) => Promise<HydratedDocument<IAccessTokenDocument> | null>
+  findUserIdByToken: (token: string, requiredScopes: Scope[]) => Promise<HydratedDocument<IAccessTokenDocument> | null>
   findTokenByUserId: (userId: Types.ObjectId | string) => Promise<HydratedDocument<IAccessTokenDocument>[] | null>
   validateTokenScopes: (token: string, requiredScopes: Scope[]) => Promise<boolean>
 }
@@ -102,14 +102,14 @@ accessTokenSchema.statics.deleteExpiredToken = async function() {
   await this.deleteMany({ expiredAt: { $lte: now } });
 };
 
-accessTokenSchema.statics.findUserIdByToken = async function(token: string, requiredScopes?: Scope[]) {
+accessTokenSchema.statics.findUserIdByToken = async function(token: string, requiredScopes: Scope[]) {
   const tokenHash = generateTokenHash(token);
   const now = new Date();
-  if (requiredScopes != null && requiredScopes.length > 0) {
-    const extractedScopes = requiredScopes.map(scope => extractAllScope(scope)).flat();
-    return this.findOne({ tokenHash, expiredAt: { $gt: now }, scopes: { $all: extractedScopes } }).select('user');
+  if (requiredScopes.length === 0) {
+    return;
   }
-  return this.findOne({ tokenHash, expiredAt: { $gt: now } }).select('user');
+  const extractedScopes = requiredScopes.map(scope => extractAllScope(scope)).flat();
+  return this.findOne({ tokenHash, expiredAt: { $gt: now }, scopes: { $all: extractedScopes } }).select('user');
 };
 
 accessTokenSchema.statics.findTokenByUserId = async function(userId: Types.ObjectId | string) {
@@ -118,10 +118,7 @@ accessTokenSchema.statics.findTokenByUserId = async function(userId: Types.Objec
 };
 
 accessTokenSchema.statics.validateTokenScopes = async function(token: string, requiredScopes: Scope[]) {
-  const tokenHash = generateTokenHash(token);
-  const now = new Date();
-  const tokenData = await this.findOne({ tokenHash, expiredAt: { $gt: now }, scopes: { $all: requiredScopes } });
-  return tokenData != null;
+  return this.findUserIdByToken(token, requiredScopes) != null;
 };
 
 accessTokenSchema.methods.isExpired = function() {

+ 2 - 3
apps/app/src/server/util/scope-utils.ts

@@ -71,7 +71,7 @@ export const extractAllScope = (scope: Scope): Scope[] => {
   getAllScopeValuesFromObj(obj).forEach((value) => {
     result.push(value);
   });
-  return result;
+  return result.filter(scope => !hasAllScope(scope));
 };
 
 
@@ -102,6 +102,5 @@ export const extractScopes = (scopes?: Scope[]): Scope[] => {
       result.add(extractedScope);
     });
   });
-  const resultArray = Array.from(result.values()).filter(scope => !hasAllScope(scope));
-  return resultArray;
+  return Array.from(result);
 };