Skip to content

Instantly share code, notes, and snippets.

@myesn
Created October 20, 2022 08:52
Show Gist options
  • Save myesn/e19ba8fce32a145a4147b5844926ce96 to your computer and use it in GitHub Desktop.
Save myesn/e19ba8fce32a145a4147b5844926ce96 to your computer and use it in GitHub Desktop.
NestJS Logto Auth Guard
import {
Injectable,
CanActivate,
ExecutionContext,
HttpStatus,
BadRequestException,
UnauthorizedException,
} from '@nestjs/common';
import { Request } from 'express';
import { ConfigService } from '@nestjs/config';
import axios from 'axios';
import { createRemoteJWKSet, jwtVerify } from 'jose';
@Injectable()
export class LogtoAuthGuard implements CanActivate {
private discoveryCache: DiscoveryResponseData;
constructor(private readonly configService: ConfigService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest() as Request;
await this.verifyAuthFromRequest(request);
return true;
}
private async verifyAuthFromRequest(request: Request) {
// 从请求头中获取令牌
const token = this.extractBearerTokenFromHeaders(request);
if (!this.discoveryCache) {
this.discoveryCache = await this.fetchDiscovery();
}
try {
const { payload } = await jwtVerify(
token,
// 使用我们从 Logto OIDC 配置信息中获取的 公共 jwks_uri 提取一个公钥集。
createRemoteJWKSet(new URL(this.discoveryCache.jwks_uri)),
{
// 令牌应由 Logto 服务器发行
issuer: this.discoveryCache.issuer,
// 该令牌的目标受众应为当前被请求的 API 地址
audience: 'http://localhost:8080', // resource
},
);
// 提取 payload 信息
request.user = { id: payload.sub };
} catch (e) {
throw new UnauthorizedException('令牌过期');
}
}
private async fetchDiscovery() {
const logtoConfig = this.configService.get('logto');
const discoveryUrl = `https://${logtoConfig.domain}/oidc/.well-known/openid-configuration`;
const response = await axios.get<DiscoveryResponseData>(discoveryUrl);
if (response.status !== HttpStatus.OK) {
throw new BadRequestException(`请求 ${discoveryUrl} 失败`);
}
return response.data;
}
// 提取 access_token
private extractBearerTokenFromHeaders(request: Request) {
// // //https://docs.logto.io/zh-cn/docs/recipes/protect-your-api/node/#%E4%BB%8E%E8%AF%B7%E6%B1%82%E5%A4%B4%E4%B8%AD%E6%8F%90%E5%8F%96%E4%BB%A4%E7%89%8C
const authorization = request.get('Authorization');
const bearerTokenIdentifier = 'Bearer';
if (!authorization) {
throw new UnauthorizedException('auth.authorization_header_missing');
}
if (!authorization.startsWith(bearerTokenIdentifier)) {
throw new UnauthorizedException(
'auth.authorization_token_type_not_supported',
);
}
return authorization.slice(bearerTokenIdentifier.length + 1);
}
}
interface DiscoveryResponseData {
jwks_uri: string;
issuer: string;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment