Skip to content

Instantly share code, notes, and snippets.

@paztek
Last active August 23, 2020 09:06
Show Gist options
  • Save paztek/fff6e7ccf2055e413be704eb4ddb7ac5 to your computer and use it in GitHub Desktop.
Save paztek/fff6e7ccf2055e413be704eb4ddb7ac5 to your computer and use it in GitHub Desktop.
nestjs-authentication-example-1
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AuthenticationModule } from './authentication/authentication.module';
@Module({
imports: [
AuthenticationModule,
],
controllers: [
AppController,
],
providers: [
AppService,
],
})
export class AppModule {}
import { CanActivate, ExecutionContext, HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { AuthenticationService } from './authentication.service';
import { Request } from 'express';
@Injectable()
export class AuthenticationGuard implements CanActivate {
constructor(
private readonly authenticationService: AuthenticationService,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request: Request = context.switchToHttp().getRequest();
const header = request.header('Authorization');
if (!header) {
throw new HttpException('Authorization: Bearer <token> header missing', HttpStatus.UNAUTHORIZED);
}
const parts = header.split(' ');
if (parts.length !== 2 || parts[0] !== 'Bearer') {
throw new HttpException('Authorization: Bearer <token> header invalid', HttpStatus.UNAUTHORIZED);
}
const token = parts[1];
try {
// Store the user on the request object if we want to retrieve it from the controllers
request['user'] = await this.authenticationService.authenticate(token);
return true;
} catch (e) {
throw new HttpException(e.message, HttpStatus.UNAUTHORIZED);
}
}
}
import { HttpModule, Module } from '@nestjs/common';
import { AuthenticationGuard } from './authentication.guard';
import { AuthenticationService } from './authentication.service';
@Module({
imports: [
HttpModule,
],
providers: [
AuthenticationGuard,
AuthenticationService,
],
exports: [
AuthenticationService,
],
})
export class AuthenticationModule {}
import { HttpService, Injectable } from '@nestjs/common';
import { User } from './user.model';
interface KeycloakUserInfoResponse {
sub: string;
email_verified: boolean;
name:string;
preferred_username: string;
given_name: string;
family_name: string,
email: string;
}
export class AuthenticationError extends Error {}
@Injectable()
export class AuthenticationService {
private readonly baseURL: string;
private readonly realm: string;
constructor(
private httpService: HttpService,
) {
this.baseURL = process.env.KEYCLOAK_BASE_URL;
this.realm = process.env.KEYCLOAK_REALM;
}
/**
* Call the OpenId Connect UserInfo endpoint on Keycloak: https://openid.net/specs/openid-connect-core-1_0.html#UserInfo
*
* If it succeeds, the token is valid and we get the user infos in the response
* If it fails, the token is invalid or expired
*/
async authenticate(accessToken: string): Promise<User> {
const url = `${this.baseURL}/realms/${this.realm}/protocol/openid-connect/userinfo`;
try {
const response = await this.httpService.get<KeycloakUserInfoResponse>(url, {
headers: {
authorization: `Bearer ${accessToken}`,
},
}).toPromise();
return {
id: response.data.sub,
username: response.data.preferred_username,
};
} catch (e) {
throw new AuthenticationError(e.message);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment