feat: support client_secret

This commit is contained in:
master 2025-02-21 03:53:46 +08:00
parent 144e4c2f97
commit 0d957dfb1c
7 changed files with 51 additions and 12 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "oidc-client-rx", "name": "oidc-client-rx",
"version": "0.1.0-alpha.4", "version": "0.1.0-alpha.5",
"homepage": "https://github.com/lonelyhentxi/oidc-client-rx", "homepage": "https://github.com/lonelyhentxi/oidc-client-rx",
"author": "lonelyhentxi", "author": "lonelyhentxi",
"description": "ReactiveX enhanced OIDC and OAuth2 protocol support for browser-based JavaScript applications", "description": "ReactiveX enhanced OIDC and OAuth2 protocol support for browser-based JavaScript applications",

View File

@ -1,9 +1,9 @@
import { InjectionToken, inject } from '@outposts/injection-js'; import { InjectionToken, inject } from '@outposts/injection-js';
import type { Router } from '@tanstack/react-router'; import type { AnyRouter } from '@tanstack/react-router';
import type { AuthFeature } from '../../features'; import type { AuthFeature } from '../../features';
import { AbstractRouter } from '../../router'; import { AbstractRouter } from '../../router';
export type TanStackRouter = Router<any, any, any, any, any, any>; export type TanStackRouter = AnyRouter;
export const TANSTACK_ROUTER = new InjectionToken<TanStackRouter>( export const TANSTACK_ROUTER = new InjectionToken<TanStackRouter>(
'TANSTACK_ROUTER' 'TANSTACK_ROUTER'

View File

@ -1,6 +1,6 @@
import { Injectable, inject } from '@outposts/injection-js'; import { Injectable, inject } from '@outposts/injection-js';
import type { Observable } from 'rxjs'; import { type Observable, of } from 'rxjs';
import { map } from 'rxjs/operators'; import { map, switchMap } from 'rxjs/operators';
import type { AuthOptions } from '../auth-options'; import type { AuthOptions } from '../auth-options';
import { AuthStateService } from '../auth-state/auth-state.service'; import { AuthStateService } from '../auth-state/auth-state.service';
import { ConfigurationService } from '../config/config.service'; import { ConfigurationService } from '../config/config.service';
@ -126,7 +126,7 @@ function checkAuth(
configId?: string configId?: string
): Observable<boolean> { ): Observable<boolean> {
return configurationService.getOpenIDConfiguration(configId).pipe( return configurationService.getOpenIDConfiguration(configId).pipe(
map((configuration) => { switchMap((configuration) => {
const isAuthenticated = const isAuthenticated =
authStateService.areAuthStorageTokensValid(configuration); authStateService.areAuthStorageTokensValid(configuration);
@ -137,13 +137,16 @@ function checkAuth(
if (!isAuthenticated) { if (!isAuthenticated) {
autoLoginService.saveRedirectRoute(configuration, url); autoLoginService.saveRedirectRoute(configuration, url);
if (authOptions) { if (authOptions) {
loginService.login(configuration, authOptions); return loginService
} else { .login(configuration, authOptions)
loginService.login(configuration); .pipe(switchMap(() => of(isAuthenticated)));
} }
return loginService
.login(configuration)
.pipe(switchMap(() => of(isAuthenticated)));
} }
return isAuthenticated; return of(isAuthenticated);
}) })
); );
} }

View File

@ -40,6 +40,12 @@ export interface OpenIdConfiguration {
* or if it contains additional audiences not trusted by the Client. * or if it contains additional audiences not trusted by the Client.
*/ */
clientId?: string; clientId?: string;
/**
* @dangerous
* @see [client secret is missing](https://github.com/damienbod/angular-auth-oidc-client/issues/399)
* The client secret. For some oidc service the must provide this.
*/
clientSecret?: string;
/** /**
* `code`, `id_token token` or `id_token`. * `code`, `id_token token` or `id_token`.
* Name of the flow which can be configured. * Name of the flow which can be configured.

View File

@ -1,5 +1,6 @@
import { Injectable, inject } from '@outposts/injection-js'; import { Injectable, inject } from '@outposts/injection-js';
import { type Observable, of, throwError } from 'rxjs'; import { BehaviorSubject, type Observable, of, throwError } from 'rxjs';
import { MockUtil } from 'src/utils/reflect';
import type { AuthOptions } from '../auth-options'; import type { AuthOptions } from '../auth-options';
import type { OpenIdConfiguration } from '../config/openid-configuration'; import type { OpenIdConfiguration } from '../config/openid-configuration';
import { StoragePersistenceService } from '../storage/storage-persistence.service'; import { StoragePersistenceService } from '../storage/storage-persistence.service';
@ -24,6 +25,7 @@ export class LoginService {
private readonly popupService = inject(PopUpService); private readonly popupService = inject(PopUpService);
@MockUtil({ implementation: () => new BehaviorSubject(undefined) })
login( login(
configuration: OpenIdConfiguration | null, configuration: OpenIdConfiguration | null,
authOptions?: AuthOptions authOptions?: AuthOptions

View File

@ -30,7 +30,7 @@ export abstract class AbstractRouter<
abstract getCurrentNavigation(): NAVIGATION; abstract getCurrentNavigation(): NAVIGATION;
} }
export class VanillaLocationRouter extends AbstractRouter { export class VanillaLocationRouter extends AbstractRouter<string> {
protected document = inject(DOCUMENT); protected document = inject(DOCUMENT);
private get location(): Location { private get location(): Location {

View File

@ -249,6 +249,7 @@ export class UrlService {
configuration: OpenIdConfiguration configuration: OpenIdConfiguration
): string | null { ): string | null {
const clientId = this.getClientId(configuration); const clientId = this.getClientId(configuration);
const clientSecret = this.getClientSecret(configuration);
if (!clientId) { if (!clientId) {
return null; return null;
@ -259,6 +260,9 @@ export class UrlService {
params = params.set('client_id', clientId); params = params.set('client_id', clientId);
params = params.set('token', token); params = params.set('token', token);
params = params.set('token_type_hint', 'access_token'); params = params.set('token_type_hint', 'access_token');
if (clientSecret) {
params = params.set('client_secret', clientSecret);
}
return params.toString(); return params.toString();
} }
@ -268,6 +272,7 @@ export class UrlService {
configuration: OpenIdConfiguration configuration: OpenIdConfiguration
): string | null { ): string | null {
const clientId = this.getClientId(configuration); const clientId = this.getClientId(configuration);
const clientSecret = this.getClientSecret(configuration);
if (!clientId) { if (!clientId) {
return null; return null;
@ -278,6 +283,9 @@ export class UrlService {
params = params.set('client_id', clientId); params = params.set('client_id', clientId);
params = params.set('token', token); params = params.set('token', token);
params = params.set('token_type_hint', 'refresh_token'); params = params.set('token_type_hint', 'refresh_token');
if (clientSecret) {
params = params.set('client_secret', clientSecret);
}
return params.toString(); return params.toString();
} }
@ -304,6 +312,7 @@ export class UrlService {
customTokenParams?: { [p: string]: string | number | boolean } customTokenParams?: { [p: string]: string | number | boolean }
): string | null { ): string | null {
const clientId = this.getClientId(configuration); const clientId = this.getClientId(configuration);
const clientSecret = this.getClientSecret(configuration);
if (!clientId) { if (!clientId) {
return null; return null;
@ -313,6 +322,9 @@ export class UrlService {
params = params.set('grant_type', 'authorization_code'); params = params.set('grant_type', 'authorization_code');
params = params.set('client_id', clientId); params = params.set('client_id', clientId);
if (clientSecret) {
params = params.set('client_secret', clientSecret);
}
if (!configuration.disablePkce) { if (!configuration.disablePkce) {
const codeVerifier = this.flowsDataService.getCodeVerifier(configuration); const codeVerifier = this.flowsDataService.getCodeVerifier(configuration);
@ -364,6 +376,7 @@ export class UrlService {
customParamsRefresh?: { [key: string]: string | number | boolean } customParamsRefresh?: { [key: string]: string | number | boolean }
): string | null { ): string | null {
const clientId = this.getClientId(configuration); const clientId = this.getClientId(configuration);
const clientSecret = this.getClientSecret(configuration);
if (!clientId) { if (!clientId) {
return null; return null;
@ -374,6 +387,9 @@ export class UrlService {
params = params.set('grant_type', 'refresh_token'); params = params.set('grant_type', 'refresh_token');
params = params.set('client_id', clientId); params = params.set('client_id', clientId);
params = params.set('refresh_token', refreshToken); params = params.set('refresh_token', refreshToken);
if (clientSecret) {
params = params.set('client_secret', clientSecret);
}
if (customParamsRefresh) { if (customParamsRefresh) {
params = this.appendCustomParams({ ...customParamsRefresh }, params); params = this.appendCustomParams({ ...customParamsRefresh }, params);
@ -851,6 +867,18 @@ export class UrlService {
return clientId; return clientId;
} }
private getClientSecret(configuration: OpenIdConfiguration): string | null {
const { clientSecret } = configuration;
if (!clientSecret) {
this.loggerService.logDebug(configuration, 'could not get clientSecret');
return null;
}
return clientSecret;
}
private appendCustomParams( private appendCustomParams(
customParams: { [key: string]: string | number | boolean }, customParams: { [key: string]: string | number | boolean },
params: HttpParams params: HttpParams