Skip to content

Commit

Permalink
feat(#31): add SigninRedirectArgs to AuthService.login()
Browse files Browse the repository at this point in the history
Fixes #31
  • Loading branch information
Badisi committed Mar 1, 2023
1 parent 756569e commit 73045db
Show file tree
Hide file tree
Showing 15 changed files with 87 additions and 68 deletions.
8 changes: 4 additions & 4 deletions projects/auth-js/oidc/mobile/mobile-navigator.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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);
}
}
4 changes: 2 additions & 2 deletions projects/auth-js/oidc/mobile/mobile-window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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()) {
Expand Down
3 changes: 0 additions & 3 deletions projects/auth-js/oidc/models/mobile-window-options.model.ts

This file was deleted.

3 changes: 3 additions & 0 deletions projects/auth-js/oidc/models/mobile-window-params.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface MobileWindowParams {
mobileWindowName?: string;
}
4 changes: 2 additions & 2 deletions projects/auth-js/oidc/models/oidc-auth-settings.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
Expand All @@ -15,7 +15,7 @@ export interface OIDCAuthSettings extends CoreAuthSettings, Pick<UserManagerSett
clientId: string;
mobileScheme?: string;
retrieveUserSession?: boolean;
navigationType?: Navigation;
desktopNavigationType?: DesktopNavigation;
logLevel?: Log;
internal?: Partial<Omit<UserManagerSettings, UsefulSettings | 'authority' | 'client_id'>>;
}
74 changes: 46 additions & 28 deletions projects/auth-js/oidc/oidc-auth-manager.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -18,7 +22,7 @@ const DEFAULT_SETTINGS: Optional<OIDCAuthSettings, 'authorityUrl' | 'clientId'>
retrieveUserSession: true,
loadUserInfo: false,
automaticSilentRenew: true,
navigationType: Navigation.REDIRECT,
desktopNavigationType: DesktopNavigation.REDIRECT,
scope: 'openid profile email phone',
logLevel: Log.NONE,
internal: {
Expand All @@ -31,6 +35,18 @@ const DEFAULT_SETTINGS: Optional<OIDCAuthSettings, 'authorityUrl' | 'clientId'>
}
};

export type LoginArgs = MobileWindowParams & PopupWindowParams & RedirectParams & Omit<ExtraSigninRequestArgs, 'redirect_uri'> & {
redirectUrl?: string;
desktopNavigationType?: DesktopNavigation;
};

export type LogoutArgs = MobileWindowParams & PopupWindowParams & RedirectParams & Omit<ExtraSignoutRequestArgs, 'post_logout_redirect_uri'> & {
redirectUrl?: string;
desktopNavigationType?: DesktopNavigation;
};

export type RenewArgs = IFrameWindowParams & ExtraSigninRequestArgs;

export class OIDCAuthManager extends AuthManager<OIDCAuthSettings> {
private idTokenSubs: AuthSubscriptions<[string | undefined]> = new AuthSubscriptions();
private accessTokenSubs: AuthSubscriptions<[string | undefined]> = new AuthSubscriptions();
Expand Down Expand Up @@ -73,7 +89,7 @@ export class OIDCAuthManager extends AuthManager<OIDCAuthSettings> {
// --- PUBLIC API(s) ---

public async init(userSettings: OIDCAuthSettings): Promise<void> {
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();
Expand Down Expand Up @@ -162,37 +178,39 @@ export class OIDCAuthManager extends AuthManager<OIDCAuthSettings> {
}
}

public async logout(redirectUrl = location.href, navigationType?: Navigation): Promise<void> {
public async logout(args?: LogoutArgs): Promise<void> {
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<boolean> {
public async login(args?: LoginArgs): Promise<boolean> {
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.');
Expand All @@ -204,18 +222,18 @@ export class OIDCAuthManager extends AuthManager<OIDCAuthSettings> {
.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<void> {
return this.signinSilent().catch(error => console.error(error));
public async renew(args?: RenewArgs): Promise<void> {
return this.signinSilent(args).catch(error => console.error(error));
}

public getSettings(): OIDCAuthSettings {
Expand Down Expand Up @@ -320,8 +338,8 @@ export class OIDCAuthManager extends AuthManager<OIDCAuthSettings> {
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)) {
Expand Down Expand Up @@ -368,7 +386,7 @@ export class OIDCAuthManager extends AuthManager<OIDCAuthSettings> {
* 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();
}
Expand All @@ -390,7 +408,7 @@ export class OIDCAuthManager extends AuthManager<OIDCAuthSettings> {
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);
Expand All @@ -408,11 +426,11 @@ export class OIDCAuthManager extends AuthManager<OIDCAuthSettings> {
]);
}

private async signinSilent(): Promise<void> {
private async signinSilent(args?: SigninSilentArgs): Promise<void> {
this.notifyRenew(true);

try {
await this.userManager?.signinSilent();
await this.userManager?.signinSilent(args);
} catch (error) {
await this.removeUser();
throw error;
Expand Down Expand Up @@ -459,8 +477,8 @@ export class OIDCAuthManager extends AuthManager<OIDCAuthSettings> {
*/
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({}, {
Expand Down
14 changes: 7 additions & 7 deletions projects/auth-js/oidc/user-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -50,8 +50,8 @@ export class UserManager extends OidcClientUserManager {

public async signoutMobile(args: SignoutMobileArgs = {}): Promise<void> {
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',
Expand All @@ -64,8 +64,8 @@ export class UserManager extends OidcClientUserManager {

public async signinMobile(args: SigninMobileArgs = {}): Promise<void> {
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',
Expand Down
2 changes: 1 addition & 1 deletion projects/demo-app/web/auth-js/src/app/app.element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}/`;
});
Expand Down
8 changes: 4 additions & 4 deletions projects/demo-app/web/common/settings/default-settings.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -21,7 +21,7 @@ export const DEFAULT_AUTH_JS_SETTINGS = (production = false): UserSettings<OIDCA
audience: 'https://dev-fijd1e9x.us.auth0.com/api/v2/'
}
},
navigationType: Navigation.REDIRECT,
desktopNavigationType: DesktopNavigation.REDIRECT,
logLevel: Log.NONE,
loginRequired: false,
retrieveUserSession: true,
Expand All @@ -39,7 +39,7 @@ export const DEFAULT_AUTH_JS_SETTINGS = (production = false): UserSettings<OIDCA
clientId: '178200751804317953@demo-app',
mobileScheme: 'demo-app',
scope: 'openid profile email phone offline_access',
navigationType: Navigation.REDIRECT,
desktopNavigationType: DesktopNavigation.REDIRECT,
logLevel: Log.NONE,
loginRequired: false,
retrieveUserSession: true,
Expand All @@ -60,7 +60,7 @@ export const DEFAULT_AUTH_JS_SETTINGS = (production = false): UserSettings<OIDCA
clientId: 'demo-app',
mobileScheme: 'demo-app',
scope: 'openid profile email phone',
navigationType: Navigation.REDIRECT,
desktopNavigationType: DesktopNavigation.REDIRECT,
logLevel: Log.NONE,
loginRequired: false,
retrieveUserSession: true,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
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 { LibrarySettingsDefinitionItem } from 'demo-app-common';

Expand Down Expand Up @@ -27,15 +27,15 @@ export const AUTH_JS_LIBRARY_SETTINGS_DEFINITION: LibrarySettingsDefinitionItem<
label: 'Extra query params',
type: 'json'
}, {
name: 'navigationType',
name: 'desktopNavigationType',
label: 'Navigation type',
type: 'list',
values: [{
label: 'REDIRECT',
value: Navigation.REDIRECT
value: DesktopNavigation.REDIRECT
}, {
label: 'POPUP',
value: Navigation.POPUP
value: DesktopNavigation.POPUP
}]
}, {
name: 'logLevel',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<demo-app-main [isRenewing]="authService.isRenewing$ | async"
[isAuthenticated]="authService.isAuthenticated$ | async"
(login)="authService.login()"
(logout)="authService.logout('/')"
(logout)="authService.logout({ redirectUrl: '/' })"
(silentRenew)="authService.renew()">

<!-- playground -->
Expand Down
2 changes: 1 addition & 1 deletion projects/ngx-auth/core/auth.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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))
);
Expand Down
Loading

0 comments on commit 73045db

Please sign in to comment.