将 Auth0 作为中间件集成到 Express.js routing-controllers authorizationChecker
Integrate Auth0 as middleware in Express.js routing-controllers authorizationChecker
我在翻译时遇到困难 Auth0's Node (Express) API quickstart to a middleware variant. In particular using TypeStack's routing-controllers library and want to integrate Auth0 using their @Authorized
decorator。
在app.ts:
const app = createExpressServer({
authorizationChecker: async (action: Action, roles: string[]) => {
const token = action.request.headers['authorization'];
// QUESTION: How to validate the bearer token here so it returns true/false
// + also respect the role ('super-duper-trooper')?
return true/false; // <----- this
},
controllers: [StatusController]
});
在status-controller.ts中我用@Authorized
装饰了一个操作+其中的一个角色:
@Controller()
export class StatusController {
@Get('/status')
getAll() {
return 'OK, anonymously accessible';
}
@Authorized('super-duper-trooper') // <----- that
@Get('/status/:id')
getOne(@Param('id') id: string) {
return 'NOT OK';
}
}
我的问题:如何配置 authorizationChecker
函数,使其既验证令牌又尊重角色(上例中的 'super-duper-trooper')?
N.B。我已经尝试将其添加为常规 Express.js 中间件 (app.use(MyMiddleware)),但它已被 authorizationChecker 函数取代。
我已经设法让 authorizationChecker
为 Express.js 中的路由控制器工作。
我通过合并 jsonwebtoken
和 jwks-rsa
库来做到这一点。
请参阅以下验证给定 JWT 的身份验证函数:
import jwt from 'jsonwebtoken';
import jwksRsa from 'jwks-rsa';
export async function AuthMiddleware(token: string, roles: string[]): Promise<boolean> {
if (!token) return false;
// Extracts the bearer token from the request headers
const bearerToken = token.split(' ')[1];
// Set up a JWKS client that retrieves the public key from Auth0, this public key will be used to challenge the
// bearer token against.
const client = jwksRsa({
jwksUri: 'https://your_jwks_uri.com/jwks.json' // For example, using Auth0 you can find this in Auth0 Applications -> Advanced Settings -> Endpoints. This should look something like this: https://yourtenant.eu.auth0.com/.well-known/jwks.json
});
const getPublicKey = (header: any, callback: any) => {
client.getSigningKey(header.kid, (err, key) => {
const signingKey = key.getPublicKey();
callback(null, signingKey);
});
}
// As jwt.verify cannot be awaited, we construct a promise that we will resolve once the JWT verification has
// finished. This way, we can simulate awaiting of the JWT verification.
let jwtVerifyPromiseResolver: (tokenValid: boolean) => void;
const jwtVerifyPromise = new Promise<boolean>(resolve => {
jwtVerifyPromiseResolver = resolve;
});
const tokenNamespace = 'your_namespace'; // The namespace you have added to the roles in your auth token in an Auth0 rule
jwt.verify(bearerToken, getPublicKey, {}, (err, decodedJwt: any) => {
let jwtValid: boolean = false;
if (err)
jwtValid = false;
else {
// When the requested endpoint requires roles, check if the decoded JWT contains those roles
if (roles && roles.length > 0) {
const userRoles = decodedJwt[`${tokenNamespace}roles`];
if (userRoles)
// Token is valid if all roles for request are present in the user's roles
jwtValid = roles.every((role) => userRoles.includes(role));
else
// Token does not contain roles, mark token as invalid
jwtValid = false;
}
jwtValid = true;
}
jwtVerifyPromiseResolver(
jwtValid
);
});
return jwtVerifyPromise;
}
这个函数可以在authorizationToken
函数中使用,像这样:
const app = createExpressServer({
authorizationChecker: async (action: Action, roles: string[]) => {
const authorizationToken = action.request.headers['authorization'];
// Wait for JWT verification to complete, returning whether the token is valid or not
return await AuthMiddleware(authorizationToken, roles);
},
controllers: [StatusController]
});
配置完成后,您可以使用 @Authorize()
或 @Authorize('role')
装饰控制器中的操作,就像您已经在做的那样。这将在每次请求操作之前触发 authorizationChecker
。
注意:从端点检索 public 密钥的整个 getPublicKey
部分也可以通过将 public 密钥放在您的代码或某处的设置中来替换。这样,您也不需要手动创建承诺来等待 JWT 验证。但是,我认为检索 public 密钥 on-demand 是更优雅的解决方案。
我在翻译时遇到困难 Auth0's Node (Express) API quickstart to a middleware variant. In particular using TypeStack's routing-controllers library and want to integrate Auth0 using their @Authorized
decorator。
在app.ts:
const app = createExpressServer({
authorizationChecker: async (action: Action, roles: string[]) => {
const token = action.request.headers['authorization'];
// QUESTION: How to validate the bearer token here so it returns true/false
// + also respect the role ('super-duper-trooper')?
return true/false; // <----- this
},
controllers: [StatusController]
});
在status-controller.ts中我用@Authorized
装饰了一个操作+其中的一个角色:
@Controller()
export class StatusController {
@Get('/status')
getAll() {
return 'OK, anonymously accessible';
}
@Authorized('super-duper-trooper') // <----- that
@Get('/status/:id')
getOne(@Param('id') id: string) {
return 'NOT OK';
}
}
我的问题:如何配置 authorizationChecker
函数,使其既验证令牌又尊重角色(上例中的 'super-duper-trooper')?
N.B。我已经尝试将其添加为常规 Express.js 中间件 (app.use(MyMiddleware)),但它已被 authorizationChecker 函数取代。
我已经设法让 authorizationChecker
为 Express.js 中的路由控制器工作。
我通过合并 jsonwebtoken
和 jwks-rsa
库来做到这一点。
请参阅以下验证给定 JWT 的身份验证函数:
import jwt from 'jsonwebtoken';
import jwksRsa from 'jwks-rsa';
export async function AuthMiddleware(token: string, roles: string[]): Promise<boolean> {
if (!token) return false;
// Extracts the bearer token from the request headers
const bearerToken = token.split(' ')[1];
// Set up a JWKS client that retrieves the public key from Auth0, this public key will be used to challenge the
// bearer token against.
const client = jwksRsa({
jwksUri: 'https://your_jwks_uri.com/jwks.json' // For example, using Auth0 you can find this in Auth0 Applications -> Advanced Settings -> Endpoints. This should look something like this: https://yourtenant.eu.auth0.com/.well-known/jwks.json
});
const getPublicKey = (header: any, callback: any) => {
client.getSigningKey(header.kid, (err, key) => {
const signingKey = key.getPublicKey();
callback(null, signingKey);
});
}
// As jwt.verify cannot be awaited, we construct a promise that we will resolve once the JWT verification has
// finished. This way, we can simulate awaiting of the JWT verification.
let jwtVerifyPromiseResolver: (tokenValid: boolean) => void;
const jwtVerifyPromise = new Promise<boolean>(resolve => {
jwtVerifyPromiseResolver = resolve;
});
const tokenNamespace = 'your_namespace'; // The namespace you have added to the roles in your auth token in an Auth0 rule
jwt.verify(bearerToken, getPublicKey, {}, (err, decodedJwt: any) => {
let jwtValid: boolean = false;
if (err)
jwtValid = false;
else {
// When the requested endpoint requires roles, check if the decoded JWT contains those roles
if (roles && roles.length > 0) {
const userRoles = decodedJwt[`${tokenNamespace}roles`];
if (userRoles)
// Token is valid if all roles for request are present in the user's roles
jwtValid = roles.every((role) => userRoles.includes(role));
else
// Token does not contain roles, mark token as invalid
jwtValid = false;
}
jwtValid = true;
}
jwtVerifyPromiseResolver(
jwtValid
);
});
return jwtVerifyPromise;
}
这个函数可以在authorizationToken
函数中使用,像这样:
const app = createExpressServer({
authorizationChecker: async (action: Action, roles: string[]) => {
const authorizationToken = action.request.headers['authorization'];
// Wait for JWT verification to complete, returning whether the token is valid or not
return await AuthMiddleware(authorizationToken, roles);
},
controllers: [StatusController]
});
配置完成后,您可以使用 @Authorize()
或 @Authorize('role')
装饰控制器中的操作,就像您已经在做的那样。这将在每次请求操作之前触发 authorizationChecker
。
注意:从端点检索 public 密钥的整个 getPublicKey
部分也可以通过将 public 密钥放在您的代码或某处的设置中来替换。这样,您也不需要手动创建承诺来等待 JWT 验证。但是,我认为检索 public 密钥 on-demand 是更优雅的解决方案。