Browse Source

ssr logout callback added

pull/23416/head
erdemcaygor 1 year ago
parent
commit
0a1dabc305
  1. 41
      npm/ng-packs/apps/dev-app/src/server.ts
  2. 60
      npm/ng-packs/packages/oauth/src/lib/strategies/auth-code-flow-strategy.ts

41
npm/ng-packs/apps/dev-app/src/server.ts

@ -28,18 +28,21 @@ const REDIRECT_URI = 'http://localhost:4200';
const SCOPE = 'offline_access MyProjectName';
const config = await oidc.discovery(ISSUER, CLIENT_ID, /* client_secret */ undefined);
const secureCookie = { httpOnly: false, sameSite: 'lax' as const, secure: environment.production, path: '/' };
const secureCookie = { httpOnly: true, sameSite: 'lax' as const, secure: environment.production, path: '/' };
const tokenCookie = { ...secureCookie, httpOnly: false, maxAge: 60 * 60 * 24 * 1 }; // 30 days
app.use(cookieParser());
const sessions = new Map<string, { pkce?: string; state?: string; refresh?: string; at?: string }>();
const sessions = new Map<string, { pkce?: string; state?: string; refresh?: string; at?: string, returnUrl?: string }>();
app.get('/authorize', async (_req, res) => {
const code_verifier = oidc.randomPKCECodeVerifier();
const code_challenge = await oidc.calculatePKCECodeChallenge(code_verifier);
const state = oidc.randomState();
const returnUrl = String(_req.query.returnUrl || null);
res.cookie('returnUrl', returnUrl, { ...secureCookie, maxAge: 5 * 60 * 1000 });
const sid = crypto.randomUUID();
sessions.set(sid, { pkce: code_verifier, state });
res.cookie('sid', sid, secureCookie);
@ -54,6 +57,36 @@ app.get('/authorize', async (_req, res) => {
res.redirect(url.toString());
});
app.get('/logout', async (req, res) => {
try {
const sid = req.cookies.sid;
if (sid && sessions.has(sid)) {
sessions.delete(sid);
}
res.clearCookie('sid', secureCookie);
res.clearCookie('access_token', tokenCookie);
res.clearCookie('refresh_token', secureCookie);
res.clearCookie('expires_at', tokenCookie);
res.clearCookie('returnUrl', secureCookie);
const endSessionEndpoint = config.serverMetadata().end_session_endpoint;
if (endSessionEndpoint) {
const logoutUrl = new URL(endSessionEndpoint);
logoutUrl.searchParams.set('post_logout_redirect_uri', REDIRECT_URI);
logoutUrl.searchParams.set('client_id', CLIENT_ID);
return res.redirect(logoutUrl.toString());
}
res.redirect('/');
} catch (error) {
console.error('Logout error:', error);
res.status(500).send('Logout error');
}
});
app.get('/', async (req, res, next) => {
try {
const { code, state } = req.query as any;
@ -98,6 +131,10 @@ app.get('/', async (req, res, next) => {
res.cookie('access_token', tokens.access_token, tokenCookie);
res.cookie('refresh_token', tokens.refresh_token, secureCookie);
res.cookie('expires_at', String(accessExpiresAt.getTime()), tokenCookie);
const returnUrl = req.cookies.returnUrl || '/';
res.clearCookie('returnUrl', secureCookie);
return res.redirect('/');
} catch (e) {
console.error('OIDC error:', e);

60
npm/ng-packs/packages/oauth/src/lib/strategies/auth-code-flow-strategy.ts

@ -1,5 +1,5 @@
import { Injector, PLATFORM_ID } from '@angular/core';
import { noop } from '@abp/ng.core';
import { DOCUMENT, Injector, PLATFORM_ID } from '@angular/core';
import { APP_STARTED_WITH_SSR, noop } from '@abp/ng.core';
import { Params } from '@angular/router';
import { filter, from, of, take, tap } from 'rxjs';
import { AuthFlowStrategy } from './auth-flow-strategy';
@ -9,25 +9,27 @@ import { isPlatformBrowser } from '@angular/common';
export class AuthCodeFlowStrategy extends AuthFlowStrategy {
readonly isInternalAuth = false;
private platformId: object;
protected appStartedWithSSR: boolean;
protected document: Document;
constructor(protected injector: Injector) {
super(injector);
this.platformId = injector.get(PLATFORM_ID);
this.appStartedWithSSR = injector.get(APP_STARTED_WITH_SSR);
this.document = injector.get(DOCUMENT);
}
async init() {
this.checkRememberMeOption();
this.listenToTokenReceived();
return super
.init()
.then(() => this.oAuthService.tryLogin().catch(noop))
.then(() => {
//TODO:Investigate silent refresh issue in SSR
if (isPlatformBrowser(this.platformId)) {
if (!this.appStartedWithSSR && isPlatformBrowser(this.platformId)) {
return super
.init()
.then(() => this.oAuthService.tryLogin().catch(noop))
.then(() => {
this.oAuthService.setupAutomaticSilentRefresh();
}
});
});
}
}
private checkRememberMeOption() {
@ -78,11 +80,12 @@ export class AuthCodeFlowStrategy extends AuthFlowStrategy {
}
protected listenToTokenReceived() {
if (isPlatformBrowser(this.platformId)) {
if (isPlatformBrowser(this.platformId) && !this.appStartedWithSSR) {
this.oAuthService.events
.pipe(
filter(event => event.type === 'token_received'),
tap(() => {
//TODO: Investigate how to handle with ssr
this.setUICulture();
this.replaceURLParams();
}),
@ -94,13 +97,19 @@ export class AuthCodeFlowStrategy extends AuthFlowStrategy {
navigateToLogin(queryParams?: Params) {
if (isPlatformBrowser(this.platformId)) {
let additionalState = '';
if (queryParams?.returnUrl) {
additionalState = queryParams.returnUrl;
}
if (APP_STARTED_WITH_SSR) {
if (this.document.defaultView) {
this.document.defaultView.location.replace('/authorize');
}
} else {
let additionalState = '';
if (queryParams?.returnUrl) {
additionalState = queryParams.returnUrl;
}
const cultureParams = this.getCultureParams(queryParams);
this.oAuthService.initCodeFlow(additionalState, cultureParams);
const cultureParams = this.getCultureParams(queryParams);
this.oAuthService.initCodeFlow(additionalState, cultureParams);
}
}
}
@ -113,11 +122,18 @@ export class AuthCodeFlowStrategy extends AuthFlowStrategy {
logout(queryParams?: Params) {
this.rememberMeService.remove();
if (queryParams?.noRedirectToLogoutUrl) {
this.router.navigate(['/']);
return from(this.oAuthService.revokeTokenAndLogout(true));
if (APP_STARTED_WITH_SSR) {
if (this.document.defaultView) {
this.document.defaultView.location.replace('/logout');
}
} else {
if (queryParams?.noRedirectToLogoutUrl) {
this.router.navigate(['/']);
return from(this.oAuthService.revokeTokenAndLogout(true));
}
return from(this.oAuthService.revokeTokenAndLogout(this.getCultureParams(queryParams)));
}
return from(this.oAuthService.revokeTokenAndLogout(this.getCultureParams(queryParams)));
}
login(queryParams?: Params) {

Loading…
Cancel
Save