diff --git a/projects/auth-js/oidc/mobile/mobile-navigator.ts b/projects/auth-js/oidc/mobile/mobile-navigator.ts index f3acc21..f33b9d4 100644 --- a/projects/auth-js/oidc/mobile/mobile-navigator.ts +++ b/projects/auth-js/oidc/mobile/mobile-navigator.ts @@ -1,6 +1,6 @@ import { Logger, UserManagerSettingsStore } from 'oidc-client-ts'; -import { MobileWindowOptions } from '../models/mobile-window-options.model'; +import { MobileWindowParams } from '../models/mobile-window-params.model'; import { MobileWindow } from './mobile-window'; export class MobileNavigator { @@ -12,9 +12,9 @@ export class MobileNavigator { // TODO: manage settings } - public prepare(options: MobileWindowOptions, redirectUrl: string): MobileWindow { - // TODO: manage options + public prepare(params: MobileWindowParams, redirectUrl: string): MobileWindow { + // TODO: manage params this._logger.create('prepare'); - return new MobileWindow(options, redirectUrl); + return new MobileWindow(params, redirectUrl); } } diff --git a/projects/auth-js/oidc/mobile/mobile-window.ts b/projects/auth-js/oidc/mobile/mobile-window.ts index 5faa8f6..a405fb9 100644 --- a/projects/auth-js/oidc/mobile/mobile-window.ts +++ b/projects/auth-js/oidc/mobile/mobile-window.ts @@ -4,7 +4,7 @@ import type { PluginListenerHandle } from '@capacitor/core'; import { Logger } from 'oidc-client-ts'; import { AuthUtils } from '../../core'; -import { MobileWindowOptions } from '../models/mobile-window-options.model'; +import { MobileWindowParams } from '../models/mobile-window-params.model'; const CUSTOM_URL_SCHEME_HANDLER_TIMEOUT = 10 * 1000; // 10s const CAPACITOR_APP = window.Capacitor?.Plugins?.App; @@ -24,7 +24,7 @@ export class MobileWindow implements IWindow { private _reject?: (reason?: unknown) => void; constructor( - public options: MobileWindowOptions, + public options: MobileWindowParams, public redirectUrl: string ) { if (!AuthUtils.isCapacitor() && !AuthUtils.isCordova()) { diff --git a/projects/auth-js/oidc/models/mobile-window-options.model.ts b/projects/auth-js/oidc/models/mobile-window-options.model.ts deleted file mode 100644 index 23f2201..0000000 --- a/projects/auth-js/oidc/models/mobile-window-options.model.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface MobileWindowOptions { - name?: string; -} diff --git a/projects/auth-js/oidc/models/mobile-window-params.model.ts b/projects/auth-js/oidc/models/mobile-window-params.model.ts new file mode 100644 index 0000000..7430f51 --- /dev/null +++ b/projects/auth-js/oidc/models/mobile-window-params.model.ts @@ -0,0 +1,3 @@ +export interface MobileWindowParams { + mobileWindowName?: string; +} diff --git a/projects/auth-js/oidc/models/oidc-auth-settings.model.ts b/projects/auth-js/oidc/models/oidc-auth-settings.model.ts index dfd86d1..e626d42 100644 --- a/projects/auth-js/oidc/models/oidc-auth-settings.model.ts +++ b/projects/auth-js/oidc/models/oidc-auth-settings.model.ts @@ -2,7 +2,7 @@ import { Log, UserManagerSettings } from 'oidc-client-ts'; import { AuthSettings as CoreAuthSettings } from '../../core'; -export enum Navigation { +export enum DesktopNavigation { REDIRECT = 'REDIRECT', POPUP = 'POPUP' } @@ -15,7 +15,7 @@ export interface OIDCAuthSettings extends CoreAuthSettings, Pick>; } diff --git a/projects/auth-js/oidc/oidc-auth-manager.ts b/projects/auth-js/oidc/oidc-auth-manager.ts index 374f2c5..69e98c9 100644 --- a/projects/auth-js/oidc/oidc-auth-manager.ts +++ b/projects/auth-js/oidc/oidc-auth-manager.ts @@ -1,13 +1,17 @@ /* eslint-disable @typescript-eslint/restrict-template-expressions, @typescript-eslint/naming-convention, camelcase */ import { merge } from 'lodash-es'; -import { ErrorResponse, InMemoryWebStorage, Log, User, UserProfile, WebStorageStateStore } from 'oidc-client-ts'; +import { + ErrorResponse, ExtraSigninRequestArgs, ExtraSignoutRequestArgs, IFrameWindowParams, InMemoryWebStorage, Log, + PopupWindowParams, RedirectParams, SigninSilentArgs, User, UserProfile, WebStorageStateStore +} from 'oidc-client-ts'; import { AuthManager, AuthSubscriber, AuthSubscription, AuthSubscriptions, AuthUtils, Optional } from '../core'; import { MobileStorage } from './mobile/mobile-storage'; import { AccessToken } from './models/access-token.model'; import { IdToken } from './models/id-token.model'; -import { Navigation, OIDCAuthSettings } from './models/oidc-auth-settings.model'; +import { MobileWindowParams } from './models/mobile-window-params.model'; +import { DesktopNavigation, OIDCAuthSettings } from './models/oidc-auth-settings.model'; import { UserSession } from './models/user-session.model'; import { UserManager } from './user-manager'; @@ -18,7 +22,7 @@ const DEFAULT_SETTINGS: Optional retrieveUserSession: true, loadUserInfo: false, automaticSilentRenew: true, - navigationType: Navigation.REDIRECT, + desktopNavigationType: DesktopNavigation.REDIRECT, scope: 'openid profile email phone', logLevel: Log.NONE, internal: { @@ -31,6 +35,18 @@ const DEFAULT_SETTINGS: Optional } }; +export type LoginArgs = MobileWindowParams & PopupWindowParams & RedirectParams & Omit & { + redirectUrl?: string; + desktopNavigationType?: DesktopNavigation; +}; + +export type LogoutArgs = MobileWindowParams & PopupWindowParams & RedirectParams & Omit & { + redirectUrl?: string; + desktopNavigationType?: DesktopNavigation; +}; + +export type RenewArgs = IFrameWindowParams & ExtraSigninRequestArgs; + export class OIDCAuthManager extends AuthManager { private idTokenSubs: AuthSubscriptions<[string | undefined]> = new AuthSubscriptions(); private accessTokenSubs: AuthSubscriptions<[string | undefined]> = new AuthSubscriptions(); @@ -73,7 +89,7 @@ export class OIDCAuthManager extends AuthManager { // --- PUBLIC API(s) --- public async init(userSettings: OIDCAuthSettings): Promise { - Log.setLevel(userSettings.logLevel || DEFAULT_SETTINGS.logLevel || Log.NONE); + Log.setLevel(userSettings.logLevel ?? DEFAULT_SETTINGS.logLevel ?? Log.NONE); Log.setLogger(console); const isNativeMobile = AuthUtils.isNativeMobile(); @@ -162,37 +178,39 @@ export class OIDCAuthManager extends AuthManager { } } - public async logout(redirectUrl = location.href, navigationType?: Navigation): Promise { + public async logout(args?: LogoutArgs): Promise { + const redirectUrl = args?.redirectUrl ?? location.href; if (AuthUtils.isNativeMobile()) { - await this.userManager?.signoutMobile(); + await this.userManager?.signoutMobile(args); await this.redirect(redirectUrl); this.postLogoutVerification(redirectUrl); } else { - switch (navigationType || this.settings.navigationType) { - case Navigation.POPUP: - await this.userManager?.signoutPopup(); + switch (args?.desktopNavigationType ?? this.settings.desktopNavigationType) { + case DesktopNavigation.POPUP: + await this.userManager?.signoutPopup(args); await this.redirect(redirectUrl); break; - case Navigation.REDIRECT: + case DesktopNavigation.REDIRECT: default: sessionStorage.setItem(REDIRECT_URL_KEY, redirectUrl); - await this.userManager?.signoutRedirect(); + await this.userManager?.signoutRedirect(args); break; } } } - public async login(redirectUrl = location.href, navigationType?: Navigation): Promise { + public async login(args?: LoginArgs): Promise { + const redirectUrl = args?.redirectUrl ?? location.href; if (AuthUtils.isNativeMobile()) { this.notifyRenew(true); - await this.userManager?.signinMobile() + await this.userManager?.signinMobile(args) .finally(() => this.notifyRenew(false)); await this.redirect(redirectUrl); } else { - switch (navigationType || this.settings.navigationType) { - case Navigation.POPUP: + switch (args?.desktopNavigationType ?? this.settings.desktopNavigationType) { + case DesktopNavigation.POPUP: this.notifyRenew(true); - await this.userManager?.signinPopup() + await this.userManager?.signinPopup(args) .catch((error: Error) => { if (error?.message === 'Attempted to navigate on a disposed window') { error = new Error('[OIDCAuthManager] Attempted to navigate on a disposed window.'); @@ -204,18 +222,18 @@ export class OIDCAuthManager extends AuthManager { .finally(() => this.notifyRenew(false)); await this.redirect(redirectUrl); break; - case Navigation.REDIRECT: + case DesktopNavigation.REDIRECT: default: sessionStorage.setItem(REDIRECT_URL_KEY, redirectUrl); - await this.userManager?.signinRedirect(); + await this.userManager?.signinRedirect(args); break; } } return (this._isAuthenticated); } - public async renew(): Promise { - return this.signinSilent().catch(error => console.error(error)); + public async renew(args?: RenewArgs): Promise { + return this.signinSilent(args).catch(error => console.error(error)); } public getSettings(): OIDCAuthSettings { @@ -320,8 +338,8 @@ export class OIDCAuthManager extends AuthManager { private assertNotInInceptionLoop(): void { [this.settings.internal?.silent_redirect_uri, this.settings.internal?.popup_redirect_uri] .forEach(uri => { - const htmlFileName = (new RegExp(/^.*\/(.*).html$/gm).exec(uri || ''))?.[1]; - const error = new Error(`[OIDCAuthManager] ${uri || 'redirect uri'} was not found.`); + const htmlFileName = (new RegExp(/^.*\/(.*).html$/gm).exec(uri ?? ''))?.[1]; + const error = new Error(`[OIDCAuthManager] ${uri ?? 'redirect uri'} was not found.`); error.stack = undefined; if (AuthUtils.isUrlMatching(location.href, uri)) { @@ -368,7 +386,7 @@ export class OIDCAuthManager extends AuthManager { * 4) at this point, user is logged-out but still inside the app and able to see it */ private postLogoutVerification(redirectUrlAskedAfterLogout: string | null): void { - const postLogoutUrl = AuthUtils.stringToURL(redirectUrlAskedAfterLogout || '/'); + const postLogoutUrl = AuthUtils.stringToURL(redirectUrlAskedAfterLogout ?? '/'); if (this.settings.loginRequired && (location.origin === postLogoutUrl.origin)) { location.reload(); } @@ -390,7 +408,7 @@ export class OIDCAuthManager extends AuthManager { await this.removeUser(); } - const redirectUrl = AuthUtils.stringToURL(url || '/'); + const redirectUrl = AuthUtils.stringToURL(url ?? '/'); // History cannot be rewritten when origin is different if (location.origin === redirectUrl.origin) { history.replaceState(history.state, '', redirectUrl.href); @@ -408,11 +426,11 @@ export class OIDCAuthManager extends AuthManager { ]); } - private async signinSilent(): Promise { + private async signinSilent(args?: SigninSilentArgs): Promise { this.notifyRenew(true); try { - await this.userManager?.signinSilent(); + await this.userManager?.signinSilent(args); } catch (error) { await this.removeUser(); throw error; @@ -459,8 +477,8 @@ export class OIDCAuthManager extends AuthManager { */ private patchAuth0Logout(): void { if (this.settings.authorityUrl.endsWith('auth0.com')) { - const { authorityUrl, clientId, navigationType } = this.settings; - const returnTo = (navigationType === Navigation.POPUP) ? + const { authorityUrl, clientId, desktopNavigationType } = this.settings; + const returnTo = (desktopNavigationType === DesktopNavigation.POPUP) ? this.settings.internal?.popup_post_logout_redirect_uri : this.settings.internal?.post_logout_redirect_uri; this.settings.internal = merge({}, { diff --git a/projects/auth-js/oidc/user-manager.ts b/projects/auth-js/oidc/user-manager.ts index 6dc6751..953d6e3 100644 --- a/projects/auth-js/oidc/user-manager.ts +++ b/projects/auth-js/oidc/user-manager.ts @@ -5,12 +5,12 @@ import { } from 'oidc-client-ts'; import { MobileNavigator } from './mobile/mobile-navigator'; -import { MobileWindowOptions } from './models/mobile-window-options.model'; +import { MobileWindowParams } from './models/mobile-window-params.model'; import { OIDCAuthSettings } from './models/oidc-auth-settings.model'; -export type SigninMobileArgs = MobileWindowOptions & ExtraSigninRequestArgs; +export type SigninMobileArgs = MobileWindowParams & ExtraSigninRequestArgs; -export type SignoutMobileArgs = MobileWindowOptions & ExtraSignoutRequestArgs; +export type SignoutMobileArgs = MobileWindowParams & ExtraSignoutRequestArgs; /** * Extended UserManager class that adds helpers and mobile capabilities @@ -50,8 +50,8 @@ export class UserManager extends OidcClientUserManager { public async signoutMobile(args: SignoutMobileArgs = {}): Promise { const logger = this._logger.create('signout'); - const { name, ...requestArgs } = args; - const handle = this._mobileNavigator.prepare({ name }, this.settings.post_logout_redirect_uri as string); + const { mobileWindowName, ...requestArgs } = args; + const handle = this._mobileNavigator.prepare({ mobileWindowName }, this.settings.post_logout_redirect_uri as string); await this._signout({ request_type: 'so:m', @@ -64,8 +64,8 @@ export class UserManager extends OidcClientUserManager { public async signinMobile(args: SigninMobileArgs = {}): Promise { const logger = this._logger.create('signin'); - const { name, ...requestArgs } = args; - const handle = this._mobileNavigator.prepare({ name }, this.settings.redirect_uri); + const { mobileWindowName, ...requestArgs } = args; + const handle = this._mobileNavigator.prepare({ mobileWindowName }, this.settings.redirect_uri); const user = await this._signin({ request_type: 'si:m', diff --git a/projects/demo-app/web/auth-js/src/app/app.element.ts b/projects/demo-app/web/auth-js/src/app/app.element.ts index 32d8417..d6fe22a 100644 --- a/projects/demo-app/web/auth-js/src/app/app.element.ts +++ b/projects/demo-app/web/auth-js/src/app/app.element.ts @@ -128,7 +128,7 @@ export class AppElement extends HTMLElement { const login = () => void manager.login(); this.demoAppMainEl.addEventListener('login', login); - const logout = () => void manager.logout('/').then(() => { + const logout = () => void manager.logout({ redirectUrl: '/' }).then(() => { const baseUrl = document.baseURI || document.querySelector('base')?.href || location.origin; location.href = (baseUrl.endsWith('/')) ? baseUrl : `${baseUrl}/`; }); diff --git a/projects/demo-app/web/common/settings/default-settings.ts b/projects/demo-app/web/common/settings/default-settings.ts index 493faa1..3b827f1 100644 --- a/projects/demo-app/web/common/settings/default-settings.ts +++ b/projects/demo-app/web/common/settings/default-settings.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { Log, Navigation, OIDCAuthSettings } from '@badisi/auth-js/oidc'; +import { DesktopNavigation, Log, OIDCAuthSettings } from '@badisi/auth-js/oidc'; import { AuthSettings } from '@badisi/ngx-auth'; import { UserSettings } from 'demo-app-common'; @@ -21,7 +21,7 @@ export const DEFAULT_AUTH_JS_SETTINGS = (production = false): UserSettings diff --git a/projects/ngx-auth/core/auth.guard.ts b/projects/ngx-auth/core/auth.guard.ts index 9117e33..d264238 100644 --- a/projects/ngx-auth/core/auth.guard.ts +++ b/projects/ngx-auth/core/auth.guard.ts @@ -83,7 +83,7 @@ export class AuthGuard implements CanLoad, CanActivate, CanActivateChild { take(1), switchMap(isAuthenticated => { if (!isAuthenticated) { - return from(this.authService.login(redirectUrl)) + return from(this.authService.login({ redirectUrl })) .pipe( catchError(() => of(false)) ); diff --git a/projects/ngx-auth/core/auth.service.ts b/projects/ngx-auth/core/auth.service.ts index 2220c25..0bf908a 100644 --- a/projects/ngx-auth/core/auth.service.ts +++ b/projects/ngx-auth/core/auth.service.ts @@ -1,7 +1,8 @@ import { Injectable, NgZone, OnDestroy } from '@angular/core'; import { Router } from '@angular/router'; import { - AccessToken, AuthSubscription, AuthUtils, IdToken, Navigation, OIDCAuthManager, UserProfile, UserSession + AccessToken, AuthSubscription, AuthUtils, IdToken, LoginArgs, LogoutArgs, OIDCAuthManager, + RenewArgs, UserProfile, UserSession } from '@badisi/auth-js/oidc'; import { Observable, ReplaySubject } from 'rxjs'; import { distinctUntilChanged, map } from 'rxjs/operators'; @@ -77,16 +78,16 @@ export class AuthService implements OnDestroy { // --- OIDCAuthManager --- - public login(redirectUrl?: string, navigationType?: Navigation): Promise { - return this.manager.login(redirectUrl, navigationType); + public login(args?: LoginArgs): Promise { + return this.manager.login(args); } - public logout(redirectUrl?: string, navigationType?: Navigation): Promise { - return this.manager.logout(redirectUrl, navigationType); + public logout(args?: LogoutArgs): Promise { + return this.manager.logout(args); } - public renew(): Promise { - return this.manager.renew(); + public renew(args?: RenewArgs): Promise { + return this.manager.renew(args); } public getSettings(): AuthSettings { diff --git a/projects/ngx-auth/core/index.ts b/projects/ngx-auth/core/index.ts index 5a9bf0b..46f207a 100644 --- a/projects/ngx-auth/core/index.ts +++ b/projects/ngx-auth/core/index.ts @@ -5,8 +5,8 @@ * SPDX-License-Identifier: GPL-3.0-only * Copyright (C) 2022 Badisi */ -export { UserSession, Navigation, Log } from '@badisi/auth-js/oidc'; -export type { UserProfile, AccessToken } from '@badisi/auth-js/oidc'; +export { UserSession, DesktopNavigation, Log } from '@badisi/auth-js/oidc'; +export type { UserProfile, AccessToken, LoginArgs, LogoutArgs, RenewArgs } from '@badisi/auth-js/oidc'; export * from './auth-settings.model'; export * from './auth.guard'; diff --git a/projects/site/docs/documentation/configuration.mdx b/projects/site/docs/documentation/configuration.mdx index 9f95a55..1f18e05 100644 --- a/projects/site/docs/documentation/configuration.mdx +++ b/projects/site/docs/documentation/configuration.mdx @@ -59,8 +59,8 @@ await initAuth(settings: AuthSettings); > **type**: boolean
> **default**: true -#### `navigationType` -> **type**: enum - *Navigation.(REDIRECT, POPUP)*
+#### `desktopNavigationType` +> **type**: enum - *DesktopNavigation.(REDIRECT, POPUP)*
> **default**: REDIRECT #### `scope`