-
Notifications
You must be signed in to change notification settings - Fork 150
/
logout.ts
153 lines (143 loc) · 6.13 KB
/
logout.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import { initSession } from './initSession.js';
import { initWorkerAsync } from './initWorker.js';
import { performRevocationRequestAsync, TOKEN_TYPE } from './requests.js';
import timer from './timer.js';
import { StringMap } from './types.js';
import {ILOidcLocation} from "./location";
import {eventNames} from "./events";
export const oidcLogoutTokens = {
access_token: 'access_token',
refresh_token: 'refresh_token',
};
const extractExtras = (extras: StringMap, postKey: string):StringMap => {
const postExtras:StringMap = {};
if (extras) {
for (const [key, value] of Object.entries(extras)) {
if (key.endsWith(postKey)) {
const newKey = key.replace(postKey, '');
postExtras[newKey] = value;
}
}
return postExtras;
}
return postExtras;
}
const keepExtras = (extras: StringMap):StringMap => {
const postExtras : StringMap = {};
if (extras) {
for (const [key, value] of Object.entries(extras)) {
if (!key.includes(':')) {
postExtras[key] = value;
}
}
return postExtras;
}
return postExtras;
}
export const destroyAsync = (oidc) => async (status) => {
timer.clearTimeout(oidc.timeoutId);
oidc.timeoutId = null;
if (oidc.checkSessionIFrame) {
oidc.checkSessionIFrame.stop();
}
const serviceWorker = await initWorkerAsync(oidc.configuration, oidc.configurationName);
if (!serviceWorker) {
const session = initSession(oidc.configurationName, oidc.configuration.storage);
await session.clearAsync(status);
} else {
await serviceWorker.clearAsync(status);
}
oidc.tokens = null;
oidc.userInfo = null;
};
export const logoutAsync = (oidc,
oidcDatabase,
fetch,
console, oicLocation:ILOidcLocation) => async (callbackPathOrUrl: string | null | undefined = undefined, extras: StringMap = null) => {
const configuration = oidc.configuration;
const oidcServerConfiguration = await oidc.initAsync(configuration.authority, configuration.authority_configuration);
if (callbackPathOrUrl && (typeof callbackPathOrUrl !== 'string')) {
callbackPathOrUrl = undefined;
console.warn('callbackPathOrUrl path is not a string');
}
const path = (callbackPathOrUrl === null || callbackPathOrUrl === undefined) ? oicLocation.getPath() : callbackPathOrUrl;
let isUri = false;
if (callbackPathOrUrl) {
isUri = callbackPathOrUrl.includes('https://') || callbackPathOrUrl.includes('http://');
}
const url = isUri ? callbackPathOrUrl : oicLocation.getOrigin() + path
// @ts-ignore
const idToken = oidc.tokens ? oidc.tokens.idToken : '';
try {
const revocationEndpoint = oidcServerConfiguration.revocationEndpoint;
if (revocationEndpoint) {
const promises = [];
const accessToken = oidc.tokens ? oidc.tokens.accessToken : null;
if (accessToken && configuration.logout_tokens_to_invalidate.includes(oidcLogoutTokens.access_token)) {
const revokeAccessTokenExtras = extractExtras(extras, ':revoke_access_token');
const revokeAccessTokenPromise = performRevocationRequestAsync(fetch)(revocationEndpoint,
accessToken,
TOKEN_TYPE.access_token,
configuration.client_id,
revokeAccessTokenExtras);
promises.push(revokeAccessTokenPromise);
}
const refreshToken = oidc.tokens ? oidc.tokens.refreshToken : null;
if (refreshToken && configuration.logout_tokens_to_invalidate.includes(oidcLogoutTokens.refresh_token)) {
const revokeAccessTokenExtras = extractExtras(extras, ':revoke_refresh_token');
const revokeRefreshTokenPromise = performRevocationRequestAsync(fetch)(revocationEndpoint,
refreshToken,
TOKEN_TYPE.refresh_token,
configuration.client_id,
revokeAccessTokenExtras);
promises.push(revokeRefreshTokenPromise);
}
if (promises.length > 0) {
await Promise.all(promises);
}
}
} catch (exception) {
console.warn('logoutAsync: error when revoking tokens, if the error persist, you ay configure property logout_tokens_to_invalidate from configuration to avoid this error');
console.warn(exception);
}
// @ts-ignore
const sub = oidc.tokens && oidc.tokens.idTokenPayload ? oidc.tokens.idTokenPayload.sub : null;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
await oidc.destroyAsync('LOGGED_OUT');
for (const [key, itemOidc] of Object.entries(oidcDatabase)) {
if (itemOidc !== oidc) {
// @ts-ignore
await oidc.logoutSameTabAsync(oidc.configuration.client_id, sub);
} else {
oidc.publishEvent(eventNames.logout_from_same_tab, {} );
}
}
const oidcExtras = extractExtras(extras, ':oidc');
let noReload = oidcExtras && oidcExtras['no_reload'] === 'true';
if(noReload) {
return;
}
const endPointExtras = keepExtras(extras);
if (oidcServerConfiguration.endSessionEndpoint) {
if (!('id_token_hint' in endPointExtras)) {
endPointExtras['id_token_hint'] = idToken;
}
if (!('post_logout_redirect_uri' in endPointExtras) && callbackPathOrUrl !== null) {
endPointExtras['post_logout_redirect_uri'] = url;
}
let queryString = '';
for (const [key, value] of Object.entries(endPointExtras)) {
if(value !== null && value !== undefined) {
if (queryString === '') {
queryString += '?';
} else {
queryString += '&';
}
queryString += `${key}=${encodeURIComponent(value)}`;
}
}
oicLocation.open(`${oidcServerConfiguration.endSessionEndpoint}${queryString}`);
} else {
oicLocation.reload();
}
};