Browse Source

Refactoring

pull/5981/head
Thomas Kaul 1 week ago
parent
commit
3d89947589
  1. 2
      apps/api/src/app/auth/auth.controller.ts
  2. 7
      apps/api/src/app/auth/auth.module.ts
  3. 19
      apps/api/src/app/auth/interfaces/interfaces.ts
  4. 11
      apps/api/src/app/auth/oidc-state.store.ts
  5. 25
      apps/api/src/app/auth/oidc.strategy.ts
  6. 16
      apps/api/src/services/configuration/configuration.service.ts
  7. 2
      apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html
  8. 0
      prisma/migrations/20251103162035_added_oidc_to_provider/migration.sql

2
apps/api/src/app/auth/auth.controller.ts

@ -111,8 +111,6 @@ export class AuthController {
StatusCodes.FORBIDDEN StatusCodes.FORBIDDEN
); );
} }
// Initiates the OIDC login flow
} }
@Get('oidc/callback') @Get('oidc/callback')

7
apps/api/src/app/auth/auth.module.ts

@ -60,6 +60,7 @@ import { OidcStrategy } from './oidc.strategy';
const response = await fetch( const response = await fetch(
`${issuer}/.well-known/openid-configuration` `${issuer}/.well-known/openid-configuration`
); );
const config = (await response.json()) as { const config = (await response.json()) as {
authorization_endpoint: string; authorization_endpoint: string;
token_endpoint: string; token_endpoint: string;
@ -67,12 +68,12 @@ import { OidcStrategy } from './oidc.strategy';
}; };
options = { options = {
issuer,
scope,
authorizationURL: config.authorization_endpoint, authorizationURL: config.authorization_endpoint,
callbackURL: callbackUrl, callbackURL: callbackUrl,
clientID: configurationService.get('OIDC_CLIENT_ID'), clientID: configurationService.get('OIDC_CLIENT_ID'),
clientSecret: configurationService.get('OIDC_CLIENT_SECRET'), clientSecret: configurationService.get('OIDC_CLIENT_SECRET'),
issuer,
scope,
tokenURL: config.token_endpoint, tokenURL: config.token_endpoint,
userInfoURL: config.userinfo_endpoint userInfoURL: config.userinfo_endpoint
}; };
@ -82,6 +83,7 @@ import { OidcStrategy } from './oidc.strategy';
} }
} else { } else {
options = { options = {
scope,
authorizationURL: configurationService.get( authorizationURL: configurationService.get(
'OIDC_AUTHORIZATION_URL' 'OIDC_AUTHORIZATION_URL'
), ),
@ -89,7 +91,6 @@ import { OidcStrategy } from './oidc.strategy';
clientID: configurationService.get('OIDC_CLIENT_ID'), clientID: configurationService.get('OIDC_CLIENT_ID'),
clientSecret: configurationService.get('OIDC_CLIENT_SECRET'), clientSecret: configurationService.get('OIDC_CLIENT_SECRET'),
issuer: configurationService.get('OIDC_ISSUER'), issuer: configurationService.get('OIDC_ISSUER'),
scope,
tokenURL: configurationService.get('OIDC_TOKEN_URL'), tokenURL: configurationService.get('OIDC_TOKEN_URL'),
userInfoURL: configurationService.get('OIDC_USER_INFO_URL') userInfoURL: configurationService.get('OIDC_USER_INFO_URL')
}; };

19
apps/api/src/app/auth/interfaces/interfaces.ts

@ -6,6 +6,25 @@ export interface AuthDeviceDialogParams {
authDevice: AuthDeviceDto; authDevice: AuthDeviceDto;
} }
export interface OidcContext {
claims?: {
sub?: string;
};
}
export interface OidcIdToken {
sub?: string;
}
export interface OidcParams {
sub?: string;
}
export interface OidcProfile {
id?: string;
sub?: string;
}
export interface ValidateOAuthLoginParams { export interface ValidateOAuthLoginParams {
provider: Provider; provider: Provider;
thirdPartyId: string; thirdPartyId: string;

11
apps/api/src/app/auth/oidc-state.store.ts

@ -5,6 +5,8 @@ import ms from 'ms';
* This store manages OAuth2 state parameters in memory with automatic cleanup. * This store manages OAuth2 state parameters in memory with automatic cleanup.
*/ */
export class OidcStateStore { export class OidcStateStore {
private readonly STATE_EXPIRY_MS = ms('10 minutes');
private stateMap = new Map< private stateMap = new Map<
string, string,
{ {
@ -14,7 +16,6 @@ export class OidcStateStore {
timestamp: number; timestamp: number;
} }
>(); >();
private readonly STATE_EXPIRY_MS = ms('10 minutes');
/** /**
* Store request state. * Store request state.
@ -26,7 +27,7 @@ export class OidcStateStore {
appState: unknown, appState: unknown,
ctx: { maxAge?: number; nonce?: string; issued?: Date }, ctx: { maxAge?: number; nonce?: string; issued?: Date },
callback: (err: Error | null, handle?: string) => void callback: (err: Error | null, handle?: string) => void
): void { ) {
try { try {
// Generate a unique handle for this state // Generate a unique handle for this state
const handle = this.generateHandle(); const handle = this.generateHandle();
@ -59,7 +60,7 @@ export class OidcStateStore {
appState?: unknown, appState?: unknown,
ctx?: { maxAge?: number; nonce?: string; issued?: Date } ctx?: { maxAge?: number; nonce?: string; issued?: Date }
) => void ) => void
): void { ) {
try { try {
const data = this.stateMap.get(handle); const data = this.stateMap.get(handle);
@ -85,7 +86,7 @@ export class OidcStateStore {
/** /**
* Clean up expired states * Clean up expired states
*/ */
private cleanup(): void { private cleanup() {
const now = Date.now(); const now = Date.now();
const expiredKeys: string[] = []; const expiredKeys: string[] = [];
@ -103,7 +104,7 @@ export class OidcStateStore {
/** /**
* Generate a cryptographically secure random handle * Generate a cryptographically secure random handle
*/ */
private generateHandle(): string { private generateHandle() {
return ( return (
Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) +
Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) +

25
apps/api/src/app/auth/oidc.strategy.ts

@ -5,27 +5,14 @@ import { Request } from 'express';
import { Strategy, type StrategyOptions } from 'passport-openidconnect'; import { Strategy, type StrategyOptions } from 'passport-openidconnect';
import { AuthService } from './auth.service'; import { AuthService } from './auth.service';
import {
OidcContext,
OidcIdToken,
OidcParams,
OidcProfile
} from './interfaces/interfaces';
import { OidcStateStore } from './oidc-state.store'; import { OidcStateStore } from './oidc-state.store';
interface OidcProfile {
id?: string;
sub?: string;
}
interface OidcContext {
claims?: {
sub?: string;
};
}
interface OidcIdToken {
sub?: string;
}
interface OidcParams {
sub?: string;
}
@Injectable() @Injectable()
export class OidcStrategy extends PassportStrategy(Strategy, 'oidc') { export class OidcStrategy extends PassportStrategy(Strategy, 'oidc') {
private static readonly stateStore = new OidcStateStore(); private static readonly stateStore = new OidcStateStore();

16
apps/api/src/services/configuration/configuration.service.ts

@ -55,17 +55,17 @@ export class ConfigurationService {
GOOGLE_SHEETS_ID: str({ default: '' }), GOOGLE_SHEETS_ID: str({ default: '' }),
GOOGLE_SHEETS_PRIVATE_KEY: str({ default: '' }), GOOGLE_SHEETS_PRIVATE_KEY: str({ default: '' }),
HOST: host({ default: DEFAULT_HOST }), HOST: host({ default: DEFAULT_HOST }),
JWT_SECRET_KEY: str({}), JWT_SECRET_KEY: str(),
MAX_ACTIVITIES_TO_IMPORT: num({ default: Number.MAX_SAFE_INTEGER }), MAX_ACTIVITIES_TO_IMPORT: num({ default: Number.MAX_SAFE_INTEGER }),
MAX_CHART_ITEMS: num({ default: 365 }), MAX_CHART_ITEMS: num({ default: 365 }),
OIDC_AUTHORIZATION_URL: str({ default: undefined }), OIDC_AUTHORIZATION_URL: str(),
OIDC_CALLBACK_URL: str({ default: undefined }), OIDC_CALLBACK_URL: str(),
OIDC_CLIENT_ID: str({ default: undefined }), OIDC_CLIENT_ID: str(),
OIDC_CLIENT_SECRET: str({ default: undefined }), OIDC_CLIENT_SECRET: str(),
OIDC_ISSUER: str({ default: undefined }), OIDC_ISSUER: str(),
OIDC_SCOPE: json({ default: ['openid'] }), OIDC_SCOPE: json({ default: ['openid'] }),
OIDC_TOKEN_URL: str({ default: undefined }), OIDC_TOKEN_URL: str(),
OIDC_USER_INFO_URL: str({ default: undefined }), OIDC_USER_INFO_URL: str(),
PORT: port({ default: DEFAULT_PORT }), PORT: port({ default: DEFAULT_PORT }),
PROCESSOR_GATHER_ASSET_PROFILE_CONCURRENCY: num({ PROCESSOR_GATHER_ASSET_PROFILE_CONCURRENCY: num({
default: DEFAULT_PROCESSOR_GATHER_ASSET_PROFILE_CONCURRENCY default: DEFAULT_PROCESSOR_GATHER_ASSET_PROFILE_CONCURRENCY

2
apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html

@ -41,7 +41,7 @@
class="mr-2" class="mr-2"
src="../assets/icons/google.svg" src="../assets/icons/google.svg"
style="height: 1rem" style="height: 1rem"
/><ng-container i18n>Sign in with Google</ng-container></a /><span i18n>Sign in with Google</span></a
> >
</div> </div>
} }

0
prisma/migrations/20251103162035_add_oidc_provider/migration.sql → prisma/migrations/20251103162035_added_oidc_to_provider/migration.sql

Loading…
Cancel
Save