Skip to content

Commit df7f030

Browse files
committed
fix OIDC login redirect flow
1 parent b1c4aa4 commit df7f030

File tree

2 files changed

+57
-16
lines changed

2 files changed

+57
-16
lines changed

src/app/core/auth/auth.service.spec.ts

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import {
5151
} from '../testing/special-group.mock';
5252
import { getMockTranslateService } from '../testing/translate.service.mock';
5353
import { createSuccessfulRemoteDataObject$ } from '../utilities/remote-data.utils';
54+
import { AuthRequestService } from './auth-request.service';
5455
import {
5556
SetUserAsIdleAction,
5657
UnsetUserAsIdleAction,
@@ -60,10 +61,9 @@ import {
6061
AuthService,
6162
IMPERSONATING_COOKIE,
6263
} from './auth.service';
63-
import { AuthRequestService } from './auth-request.service';
64-
import { AuthMethod } from './models/auth.method';
6564
import { AuthStatus } from './models/auth-status.model';
6665
import { AuthTokenInfo } from './models/auth-token-info.model';
66+
import { AuthMethod } from './models/auth.method';
6767
import {
6868
getAuthenticationToken,
6969
isAuthenticated,
@@ -447,6 +447,45 @@ describe('AuthService test', () => {
447447
expect(hardRedirectService.redirect).toHaveBeenCalledWith(jasmine.stringMatching(new RegExp('reload/[0-9]*\\?redirect=' + encodeURIComponent('/home'))));
448448
});
449449

450+
it('should replace a top-level external redirectUrl', () => {
451+
const externalServerUrl = authService.getExternalServerRedirectUrl(
452+
'https://dspace.test',
453+
'/itemtemplate',
454+
'/api/authn/shibboleth?redirectUrl=https://dspace.test/home',
455+
);
456+
457+
expect(externalServerUrl).toBe('/api/authn/shibboleth?redirectUrl=https%3A%2F%2Fdspace.test%2Fitemtemplate');
458+
});
459+
460+
it('should inject redirectUrl into a nested redirect_uri for OIDC providers', () => {
461+
const externalServerUrl = authService.getExternalServerRedirectUrl(
462+
'https://dspace.test',
463+
'/itemtemplate',
464+
'https://provider.example/authorize?client_id=test-client&response_type=code&scope=openid%20email&redirect_uri=' +
465+
encodeURIComponent('https://rest.test/api/authn/oidc'),
466+
);
467+
468+
const providerUrl = new URL(externalServerUrl);
469+
const redirectUri = new URL(providerUrl.searchParams.get('redirect_uri'));
470+
471+
expect(redirectUri.toString()).toBe('https://rest.test/api/authn/oidc?redirectUrl=https%3A%2F%2Fdspace.test%2Fitemtemplate');
472+
});
473+
474+
it('should preserve existing redirect_uri query params when injecting redirectUrl', () => {
475+
const externalServerUrl = authService.getExternalServerRedirectUrl(
476+
'https://dspace.test',
477+
'/itemtemplate',
478+
'https://provider.example/authorize?client_id=test-client&response_type=code&scope=openid%20email&redirect_uri=' +
479+
encodeURIComponent('https://rest.test/api/authn/orcid?foo=bar'),
480+
);
481+
482+
const providerUrl = new URL(externalServerUrl);
483+
const redirectUri = new URL(providerUrl.searchParams.get('redirect_uri'));
484+
485+
expect(redirectUri.searchParams.get('foo')).toBe('bar');
486+
expect(redirectUri.searchParams.get('redirectUrl')).toBe('https://dspace.test/itemtemplate');
487+
});
488+
450489
it('should redirect to regular reload and not to /login', () => {
451490
authService.navigateToRedirectUrl('/login');
452491
// Reload without a redirect URL

src/app/core/auth/auth.service.ts

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import {
2323
Store,
2424
} from '@ngrx/store';
2525
import { TranslateService } from '@ngx-translate/core';
26-
import Cookies from 'js-cookie';
2726
import {
2827
Observable,
2928
of,
@@ -63,6 +62,7 @@ import {
6362
import { PageInfo } from '../shared/page-info.model';
6463
import { URLCombiner } from '../url-combiner/url-combiner';
6564
import { createSuccessfulRemoteDataObject$ } from '../utilities/remote-data.utils';
65+
import { AuthRequestService } from './auth-request.service';
6666
import {
6767
CheckAuthenticationTokenAction,
6868
RefreshTokenAction,
@@ -72,13 +72,12 @@ import {
7272
SetUserAsIdleAction,
7373
UnsetUserAsIdleAction,
7474
} from './auth.actions';
75-
import { AuthRequestService } from './auth-request.service';
76-
import { AuthMethod } from './models/auth.method';
7775
import { AuthStatus } from './models/auth-status.model';
7876
import {
7977
AuthTokenInfo,
8078
TOKENITEM,
8179
} from './models/auth-token-info.model';
80+
import { AuthMethod } from './models/auth.method';
8281
import {
8382
getAuthenticatedUserId,
8483
getAuthenticationToken,
@@ -613,20 +612,23 @@ export class AuthService {
613612
*/
614613
getExternalServerRedirectUrl(origin: string, redirectRoute: string, location: string): string {
615614
const correctRedirectUrl = new URLCombiner(origin, redirectRoute).toString();
615+
const externalServerUrl = new URL(location, origin);
616+
617+
if (externalServerUrl.searchParams.has('redirectUrl')) {
618+
externalServerUrl.searchParams.set('redirectUrl', correctRedirectUrl);
619+
} else if (externalServerUrl.searchParams.has('redirect_uri')) {
620+
const redirectUri = new URL(externalServerUrl.searchParams.get('redirect_uri'), origin);
621+
redirectUri.searchParams.set('redirectUrl', correctRedirectUrl);
622+
externalServerUrl.searchParams.set('redirect_uri', redirectUri.toString());
623+
} else {
624+
externalServerUrl.searchParams.set('redirectUrl', correctRedirectUrl);
625+
}
616626

617-
let externalServerUrl = location;
618-
const myRegexp = /\?redirectUrl=(.*)/g;
619-
const match = myRegexp.exec(location);
620-
const redirectUrlFromServer = (match && match[1]) ? match[1] : null;
621-
622-
// Check whether the current page is different from the redirect url received from rest
623-
if (isNotNull(redirectUrlFromServer) && redirectUrlFromServer !== correctRedirectUrl) {
624-
// change the redirect url with the current page url
625-
const newRedirectUrl = `?redirectUrl=${correctRedirectUrl}`;
626-
externalServerUrl = location.replace(/\?redirectUrl=(.*)/g, newRedirectUrl);
627+
if (isNotNull(location.match(/^https?:\/\//))) {
628+
return externalServerUrl.toString();
627629
}
628630

629-
return externalServerUrl;
631+
return `${externalServerUrl.pathname}${externalServerUrl.search}${externalServerUrl.hash}`;
630632
}
631633

632634
/**

0 commit comments

Comments
 (0)