feat(auth): add OIDC state parameter

Signed-off-by: Ivan Li <ivanli2048@gmail.com>
This commit is contained in:
Ivan Li 2024-10-14 13:12:30 +08:00
parent 8b6bedab39
commit 6add5ae783
No known key found for this signature in database
GPG key ID: 02223CC3543DFFD3
3 changed files with 26 additions and 1 deletions

View file

@ -41,12 +41,15 @@ export class OidcController {
@Param('oidcIdentifier') oidcIdentifier: string,
): { url: string } {
const code = this.oidcService.generateCode();
const state = this.oidcService.generateState();
request.session.oidcLoginCode = code;
request.session.oidcLoginState = state;
request.session.authProviderType = ProviderType.OIDC;
request.session.authProviderIdentifier = oidcIdentifier;
const authorizationUrl = this.oidcService.getAuthorizationUrl(
oidcIdentifier,
code,
state,
);
return { url: authorizationUrl };
}

View file

@ -119,14 +119,28 @@ export class OidcService {
return generators.codeVerifier();
}
/**
* Generates a random state for the OIDC login.
*
* @returns {string} The generated state.
*/
generateState(): string {
return generators.state();
}
/**
* Generates the authorization URL for the given OIDC identifier and code.
*
* @param {string} oidcIdentifier The identifier of the OIDC configuration
* @param {string} code The code verifier generated for the login
* @param {string} state The state generated for the login
* @returns {string} The generated authorization URL
*/
getAuthorizationUrl(oidcIdentifier: string, code: string): string {
getAuthorizationUrl(
oidcIdentifier: string,
code: string,
state: string,
): string {
const clientConfig = this.clientConfigs.get(oidcIdentifier);
if (!clientConfig) {
throw new NotFoundException(
@ -139,6 +153,7 @@ export class OidcService {
/* eslint-disable @typescript-eslint/naming-convention */
code_challenge: generators.codeChallenge(code),
code_challenge_method: 'S256',
state,
/* eslint-enable @typescript-eslint/naming-convention */
});
}
@ -166,15 +181,18 @@ export class OidcService {
const oidcConfig = clientConfig.config;
const params = client.callbackParams(request);
const code = request.session.oidcLoginCode;
const state = request.session.oidcLoginState;
const isAutodiscovered = clientConfig.config.authorizeUrl === undefined;
const tokenSet = isAutodiscovered
? await client.callback(clientConfig.redirectUri, params, {
// eslint-disable-next-line @typescript-eslint/naming-convention
code_verifier: code,
state,
})
: await client.oauthCallback(clientConfig.redirectUri, params, {
// eslint-disable-next-line @typescript-eslint/naming-convention
code_verifier: code,
state,
});
request.session.oidcIdToken = tokenSet.id_token;
@ -214,6 +232,7 @@ export class OidcService {
request.session.newUserData = newUserData;
// Cleanup: The code isn't necessary anymore
request.session.oidcLoginCode = undefined;
request.session.oidcLoginState = undefined;
return newUserData;
}

View file

@ -43,6 +43,9 @@ export interface SessionState {
/** The (random) OIDC code for verifying that OIDC responses match the OIDC requests */
oidcLoginCode?: string;
/** The (random) OIDC state for verifying that OIDC responses match the OIDC requests */
oidcLoginState?: string;
/** The user id as provided from the external auth provider, required for matching to a HedgeDoc identity */
providerUserId?: string;