Browse Source
Bugfix/harden authentication with OIDC (#7797)
* Harden authentication with OIDC
* Update changelog
---------
Co-authored-by: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
pull/7878/head
Shreya
2 days ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with
25 additions and
9 deletions
-
CHANGELOG.md
-
apps/api/src/app/auth/oidc-state.store.ts
-
apps/api/src/guards/oauth-callback.guard.ts
|
|
|
@ -25,6 +25,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 |
|
|
|
- Upgraded `papaparse` from version `5.5.3` to `5.7.0` |
|
|
|
- Upgraded `zone.js` from version `0.16.2` to `0.16.3` |
|
|
|
|
|
|
|
### Fixed |
|
|
|
|
|
|
|
- Hardened the authentication with _OpenID Connect_ (`OIDC`) (experimental) |
|
|
|
|
|
|
|
## 3.69.0 - 2026-09-07 |
|
|
|
|
|
|
|
### Changed |
|
|
|
|
|
|
|
@ -1,5 +1,6 @@ |
|
|
|
import type { Request } from 'express'; |
|
|
|
import ms from 'ms'; |
|
|
|
import { randomBytes } from 'node:crypto'; |
|
|
|
import type { |
|
|
|
SessionStore, |
|
|
|
SessionStoreCallback, |
|
|
|
@ -66,13 +67,20 @@ export class OidcStateStore implements SessionStore { |
|
|
|
const data = this.stateMap.get(handle); |
|
|
|
|
|
|
|
if (!data) { |
|
|
|
return callback(null, undefined, undefined); |
|
|
|
return callback( |
|
|
|
new Error('Invalid OIDC state parameter'), |
|
|
|
undefined, |
|
|
|
undefined |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
if (Date.now() - data.timestamp > this.STATE_EXPIRY_MS) { |
|
|
|
// State has expired
|
|
|
|
this.stateMap.delete(handle); |
|
|
|
return callback(null, undefined, undefined); |
|
|
|
return callback( |
|
|
|
new Error('OIDC state has expired, please try again'), |
|
|
|
undefined, |
|
|
|
undefined |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
// Remove state after verification (one-time use)
|
|
|
|
@ -106,10 +114,6 @@ export class OidcStateStore implements SessionStore { |
|
|
|
* Generate a cryptographically secure random handle |
|
|
|
*/ |
|
|
|
private generateHandle() { |
|
|
|
return ( |
|
|
|
Math.random().toString(36).substring(2, 15) + |
|
|
|
Math.random().toString(36).substring(2, 15) + |
|
|
|
Date.now().toString(36) |
|
|
|
); |
|
|
|
return randomBytes(32).toString('hex'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -5,11 +5,19 @@ export function OAuthCallbackGuard(strategy: string): Type<IAuthGuard> { |
|
|
|
class OAuthCallbackGuardMixin extends AuthGuard(strategy) { |
|
|
|
private readonly logger = new Logger(OAuthCallbackGuard.name); |
|
|
|
|
|
|
|
public override handleRequest(error: Error, user: any) { |
|
|
|
public override handleRequest( |
|
|
|
error: Error | null, |
|
|
|
user: any, |
|
|
|
info?: { message?: string } |
|
|
|
) { |
|
|
|
if (error) { |
|
|
|
this.logger.error( |
|
|
|
`Authentication with the ${strategy} strategy has failed: ${error.message}` |
|
|
|
); |
|
|
|
} else if (!user && info?.message) { |
|
|
|
this.logger.warn( |
|
|
|
`Authentication with the ${strategy} strategy was rejected: ${info.message}` |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
// Do not throw, the callback handler redirects to the login page instead
|
|
|
|
|