-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
proof of concept for oidc against rea id and then using that to auth …
…against backend
- Loading branch information
1 parent
42e4dcf
commit e709ea0
Showing
10 changed files
with
283 additions
and
12 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { ConfigService } from '@nestjs/config'; | ||
import { PassportStrategy } from '@nestjs/passport'; | ||
import { passportJwtSecret } from 'jwks-rsa'; | ||
import { ExtractJwt, Strategy } from 'passport-jwt'; | ||
|
||
@Injectable() | ||
export class JwtStrategy extends PassportStrategy(Strategy) { | ||
constructor(private configService: ConfigService) { | ||
super({ | ||
secretOrKeyProvider: passportJwtSecret({ | ||
cache: true, | ||
rateLimit: true, | ||
jwksRequestsPerMinute: 5, | ||
jwksUri: 'https://id.realliance.net/application/o/profile/jwks/', | ||
}), | ||
|
||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), | ||
audience: 'LNXdUuOZUue5HPlw8Vyglo83sIYndFaGUCIdQrSZ', | ||
issuer: 'https://id.realliance.net/application/o/profile/', | ||
algorithms: ['RS256'], | ||
}); | ||
} | ||
|
||
validate(payload: unknown): unknown { | ||
return payload; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { Controller, Get, UseGuards } from '@nestjs/common'; | ||
import { AuthGuard } from '@nestjs/passport'; | ||
|
||
@Controller() | ||
export class UserController { | ||
constructor() {} | ||
|
||
@Get('profile') | ||
@UseGuards(AuthGuard('jwt')) | ||
getProfile(): string { | ||
return 'OK'; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import * as oauth from 'oauth4webapi'; | ||
import Cookies from 'js-cookie'; | ||
|
||
const ISSUER = new URL('https://id.realliance.net/application/o/profile/'); | ||
const REDIRECT_URI = import.meta.env.PROD ? "https://profile.realliance.net" : "http://localhost:8080"; | ||
|
||
const as = await oauth | ||
.discoveryRequest(ISSUER) | ||
.then((response) => oauth.processDiscoveryResponse(ISSUER, response)); | ||
|
||
const client: oauth.Client = { | ||
client_id: 'LNXdUuOZUue5HPlw8Vyglo83sIYndFaGUCIdQrSZ', | ||
token_endpoint_auth_method: 'none', | ||
} | ||
|
||
export async function beginAuthFlow() { | ||
|
||
|
||
if (as.code_challenge_methods_supported?.includes('S256') !== true) { | ||
// This example assumes S256 PKCE support is signalled | ||
// If it isn't supported, random `nonce` must be used for CSRF protection. | ||
throw new Error("S256 PKCE not supported"); | ||
} | ||
|
||
const code_verifier = oauth.generateRandomCodeVerifier() | ||
const code_challenge = await oauth.calculatePKCECodeChallenge(code_verifier) | ||
const code_challenge_method = 'S256' | ||
|
||
Cookies.remove('codeVerifier'); | ||
Cookies.set('codeVerifier', code_verifier); | ||
|
||
const authorizationUrl = new URL(as.authorization_endpoint!) | ||
authorizationUrl.searchParams.set('client_id', client.client_id) | ||
authorizationUrl.searchParams.set('code_challenge', code_challenge) | ||
authorizationUrl.searchParams.set('code_challenge_method', code_challenge_method) | ||
authorizationUrl.searchParams.set('redirect_uri', REDIRECT_URI) | ||
authorizationUrl.searchParams.set('response_type', 'code') | ||
authorizationUrl.searchParams.set('scope', 'openid profile') | ||
|
||
window.location.href = authorizationUrl.toString(); | ||
} | ||
|
||
export async function onRedirect() { | ||
const currentUrl = new URL(window.location.href); | ||
const params = oauth.validateAuthResponse(as, client, currentUrl, oauth.skipStateCheck) | ||
if (oauth.isOAuth2Error(params)) { | ||
console.log('error', params) | ||
throw new Error("Oauth2 Failed") // Handle OAuth 2.0 redirect error | ||
} | ||
|
||
const code_verifier = Cookies.get('codeVerifier') ?? ""; | ||
Cookies.remove('codeVerifier'); | ||
|
||
const response = await oauth.authorizationCodeGrantRequest( | ||
as, | ||
client, | ||
params, | ||
REDIRECT_URI, | ||
code_verifier, | ||
) | ||
|
||
let challenges: oauth.WWWAuthenticateChallenge[] | undefined | ||
if ((challenges = oauth.parseWwwAuthenticateChallenges(response))) { | ||
for (const challenge of challenges) { | ||
console.log('challenge', challenge) | ||
} | ||
throw new Error() // Handle www-authenticate challenges as needed | ||
} | ||
|
||
const result = await oauth.processAuthorizationCodeOpenIDResponse(as, client, response) | ||
if (oauth.isOAuth2Error(result)) { | ||
console.log('error', result) | ||
throw new Error() // Handle OAuth 2.0 response body error | ||
} | ||
|
||
console.log('result', result) | ||
const claims = oauth.getValidatedIdTokenClaims(result) | ||
console.log('ID Token Claims', claims) | ||
|
||
const res = await fetch('http://localhost:3000/api/profile', { | ||
headers: { | ||
"Authorization": `Bearer ${result.access_token}`, | ||
} | ||
}); | ||
|
||
console.log(await res.text()); | ||
} |
Oops, something went wrong.