piral-oauth2 1.0.0-pre.2217 → 1.0.1-beta.5640

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/setup.ts CHANGED
@@ -1,76 +1,6 @@
1
- import * as ClientOAuth2 from 'client-oauth2';
2
-
3
- /**
4
- * Available configuration options for the OAuth 2.0 plugin.
5
- */
6
- export interface OAuth2Config {
7
- /**
8
- * The id of the client. Required for the setup of OAuth 2.0.
9
- */
10
- clientId: string;
11
- /**
12
- * The client secret. Only required for the `code` flow.
13
- */
14
- clientSecret?: string;
15
- /**
16
- * The Uri pointing to the authorization endpoint of the Identity Provider.
17
- */
18
- authorizationUri: string;
19
- /**
20
- * The Uri pointing to the access token endpoint of the Identity Provider.
21
- */
22
- accessTokenUri?: string;
23
- /**
24
- * The redirect Uri to use. By default the origin with /auth
25
- * is used.
26
- */
27
- redirectUri?: string;
28
- /**
29
- * The scopes to be used.
30
- */
31
- scopes?: Array<string>;
32
- /**
33
- * The OAuth 2.0 authorization flow type to be used.
34
- */
35
- flow?: 'implicit' | 'code';
36
- /**
37
- * Restricts token sharing such that other integrations, e.g., with
38
- * fetch would need to be done manually.
39
- * Otherwise, the client is responsive to the `before-fetch` event.
40
- */
41
- restrict?: boolean;
42
- }
43
-
44
- export interface OAuth2Request {
45
- /**
46
- * Sets the headers of the request.
47
- * @param headers Headers or a promise to headers.
48
- */
49
- setHeaders(headers: any): void;
50
- }
51
-
52
- export interface OAuth2Client {
53
- /**
54
- * Performs a login.
55
- */
56
- login(): void;
57
- /**
58
- * Performs a logout.
59
- */
60
- logout(): void;
61
- /**
62
- * Gets a token.
63
- */
64
- token(): Promise<string>;
65
- /**
66
- * Checks if the user is currently logged in.
67
- */
68
- account(): boolean;
69
- /**
70
- * Extends the headers of the provided request.
71
- */
72
- extendHeaders(req: OAuth2Request): void;
73
- }
1
+ import ClientOAuth2 from 'client-oauth2';
2
+ import { createOAuth2MemoryPersistence } from './utils';
3
+ import { OAuth2Config, OAuth2Client } from './types';
74
4
 
75
5
  const callbackName = 'oauth2Cb';
76
6
 
@@ -87,8 +17,14 @@ export function setupOAuth2Client(config: OAuth2Config): OAuth2Client {
87
17
  redirectUri = `${location.origin}/auth`,
88
18
  scopes = [],
89
19
  flow,
20
+ headers,
21
+ query,
22
+ state,
90
23
  restrict = false,
24
+ returnPath = '/',
25
+ persist = createOAuth2MemoryPersistence(),
91
26
  } = config;
27
+
92
28
  const client = new ClientOAuth2({
93
29
  clientId,
94
30
  clientSecret,
@@ -96,66 +32,94 @@ export function setupOAuth2Client(config: OAuth2Config): OAuth2Client {
96
32
  authorizationUri,
97
33
  accessTokenUri,
98
34
  scopes,
35
+ headers,
36
+ query,
37
+ state,
99
38
  });
39
+
100
40
  let currentToken: ClientOAuth2.Token;
101
41
  let retrieveToken: () => Promise<string>;
102
42
  let getLoginUri: () => string;
103
43
 
104
- if (flow === 'code') {
105
- client.code.getToken(location.href).then(
106
- (token) => (currentToken = token),
107
- () => {},
108
- );
44
+ const setCurrentToken = (token: ClientOAuth2.Token) => {
45
+ persist.save({
46
+ accessToken: token.accessToken,
47
+ data: token.data,
48
+ refreshToken: token.refreshToken,
49
+ });
109
50
 
110
- retrieveToken = () => {
51
+ currentToken = token;
52
+ };
53
+
54
+ const retrieve = (init: Promise<void>, refresh: () => Promise<ClientOAuth2.Token>) => {
55
+ return init.then(() => {
111
56
  if (!currentToken) {
112
57
  return Promise.reject('Not logged in. Please call `login()` to retrieve a token.');
113
58
  }
114
59
 
115
60
  if (!currentToken.expired()) {
116
- return Promise.resolve(currentToken.accessToken);
61
+ return currentToken.accessToken;
117
62
  }
118
63
 
119
- return currentToken.refresh().then((refreshedToken) => {
120
- currentToken = refreshedToken;
64
+ return refresh().then((refreshedToken) => {
65
+ setCurrentToken(refreshedToken);
121
66
  return currentToken.accessToken;
122
67
  });
68
+ });
69
+ };
70
+
71
+ const initialize = (load: () => Promise<ClientOAuth2.Token>) => {
72
+ const info = persist.load();
73
+
74
+ if (info) {
75
+ currentToken = client.createToken(info.accessToken, info.refreshToken, info.data);
76
+ return Promise.resolve();
77
+ } else {
78
+ return load().then(
79
+ (token) => {
80
+ const opener = window.opener;
81
+
82
+ setCurrentToken(token);
83
+
84
+ if (opener && typeof opener[callbackName] === 'function') {
85
+ opener[callbackName](token);
86
+ window.close();
87
+ }
88
+ },
89
+ () => {},
90
+ );
91
+ }
92
+ };
93
+
94
+ if (flow === 'code') {
95
+ const init = initialize(() => {
96
+ const url = location.href;
97
+ history.replaceState(undefined, undefined, returnPath);
98
+ return client.code.getToken(url);
99
+ });
100
+
101
+ retrieveToken = () => {
102
+ return retrieve(init, () => currentToken.refresh());
123
103
  };
124
104
  getLoginUri = () => client.code.getUri();
125
105
  } else {
126
- client.token.getToken(location.href).then(
127
- (token) => {
128
- const opener = window.opener;
129
- if (opener && typeof opener[callbackName] === 'function') {
130
- opener[callbackName](token);
131
- window.close();
132
- }
133
- currentToken = token;
134
- },
135
- () => {},
136
- );
106
+ const init = initialize(() => client.token.getToken(location.href));
137
107
 
138
108
  retrieveToken = () => {
139
- if (!currentToken) {
140
- return Promise.reject('Not logged in. Please call `login()` to retrieve a token.');
141
- }
142
-
143
- if (!currentToken.expired()) {
144
- return Promise.resolve(currentToken.accessToken);
145
- }
146
-
147
- return new Promise<string>((res) => {
148
- window[callbackName] = (token: ClientOAuth2.Token) => {
149
- currentToken = token;
150
- res(currentToken.accessToken);
151
- };
152
- window.open(client.token.getUri());
153
- });
109
+ return retrieve(
110
+ init,
111
+ () =>
112
+ new Promise<ClientOAuth2.Token>((resolve) => {
113
+ window[callbackName] = resolve;
114
+ window.open(client.token.getUri());
115
+ }),
116
+ );
154
117
  };
155
118
  getLoginUri = () => client.token.getUri();
156
119
  }
157
120
 
158
121
  return {
122
+ _: client,
159
123
  login() {
160
124
  window.location.href = getLoginUri();
161
125
  },
package/src/types.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type { Data } from 'client-oauth2';
2
+
1
3
  declare module 'piral-core/lib/types/custom' {
2
4
  interface PiletCustomApi extends PiralOAuth2Api {}
3
5
  }
@@ -8,3 +10,124 @@ export interface PiralOAuth2Api {
8
10
  */
9
11
  getAccessToken(): Promise<string | undefined>;
10
12
  }
13
+
14
+ /**
15
+ * The relevant OAuth 2 token information.
16
+ */
17
+ export interface OAuth2TokenInfo {
18
+ accessToken: string;
19
+ refreshToken: string;
20
+ data: Data;
21
+ }
22
+
23
+ /**
24
+ * Available configuration options for the OAuth 2 plugin.
25
+ */
26
+ export interface OAuth2Config {
27
+ /**
28
+ * The id of the client. Required for the setup of OAuth 2.
29
+ */
30
+ clientId: string;
31
+ /**
32
+ * The client secret. Only required for the `code` flow.
33
+ */
34
+ clientSecret?: string;
35
+ /**
36
+ * The Uri pointing to the authorization endpoint of the Identity Provider.
37
+ */
38
+ authorizationUri: string;
39
+ /**
40
+ * The Uri pointing to the access token endpoint of the Identity Provider.
41
+ */
42
+ accessTokenUri?: string;
43
+ /**
44
+ * The redirect Uri to use. By default the origin with /auth
45
+ * is used.
46
+ */
47
+ redirectUri?: string;
48
+ /**
49
+ * The return path to use in case of the "code" flow. By default the
50
+ * path will be set to "/".
51
+ */
52
+ returnPath?: string;
53
+ /**
54
+ * The scopes to be used.
55
+ */
56
+ scopes?: Array<string>;
57
+ /**
58
+ * The OAuth 2 authorization flow type to be used.
59
+ */
60
+ flow?: 'implicit' | 'code';
61
+ /**
62
+ * Restricts token sharing such that other integrations, e.g., with
63
+ * fetch would need to be done manually.
64
+ * Otherwise, the client is responsive to the `before-fetch` event.
65
+ */
66
+ restrict?: boolean;
67
+ /**
68
+ * Optional persistence layer for OAuth 2. By default nothing is stored.
69
+ */
70
+ persist?: OAuth2Persistence;
71
+ /**
72
+ * The optional headers to supply in OAuth 2 requests.
73
+ */
74
+ headers?: Record<string, string | Array<string>>;
75
+ /**
76
+ * The optional query parameters to supply in OAuth 2 requests.
77
+ */
78
+ query?: Record<string, string | Array<string>>;
79
+ /**
80
+ * The optional state parameter to supply in OAuth 2 requests.
81
+ */
82
+ state?: string;
83
+ }
84
+
85
+ export interface OAuth2Request {
86
+ /**
87
+ * Sets the headers of the request.
88
+ * @param headers Headers or a promise to headers.
89
+ */
90
+ setHeaders(headers: any): void;
91
+ }
92
+
93
+ export interface OAuth2Client {
94
+ /**
95
+ * The underlying OAuth2 client.
96
+ */
97
+ _: any;
98
+ /**
99
+ * Performs a login.
100
+ */
101
+ login(): void;
102
+ /**
103
+ * Performs a logout.
104
+ */
105
+ logout(): void;
106
+ /**
107
+ * Gets a token.
108
+ */
109
+ token(): Promise<string>;
110
+ /**
111
+ * Checks if the user is currently logged in.
112
+ */
113
+ account(): boolean;
114
+ /**
115
+ * Extends the headers of the provided request.
116
+ */
117
+ extendHeaders(req: OAuth2Request): void;
118
+ }
119
+
120
+ /**
121
+ * Defines the interface for the OAuth 2 persistence layer.
122
+ */
123
+ export interface OAuth2Persistence {
124
+ /**
125
+ * Loads an OAuth 2 token structure.
126
+ */
127
+ load(): OAuth2TokenInfo;
128
+ /**
129
+ * Stores an OAuth 2 token structure.
130
+ * @param info The token infos to store.
131
+ */
132
+ save(info: OAuth2TokenInfo): void;
133
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,61 @@
1
+ import { OAuth2Persistence } from './types';
2
+
3
+ /**
4
+ * Creates an OAuth 2 persistence layer using memory.
5
+ */
6
+ export function createOAuth2MemoryPersistence(): OAuth2Persistence {
7
+ return {
8
+ load() {
9
+ return undefined;
10
+ },
11
+ save() {},
12
+ };
13
+ }
14
+
15
+ /**
16
+ * Creates an OAuth 2 persistence layer using sessionStorage.
17
+ */
18
+ export function createOAuth2SessionPersistence(sessionKey = '$piral_oauth2_info'): OAuth2Persistence {
19
+ return {
20
+ load() {
21
+ const content = sessionStorage.getItem(sessionKey);
22
+
23
+ if (typeof content === 'string') {
24
+ try {
25
+ return JSON.parse(content);
26
+ } catch {
27
+ console.error('Found invalid data in the OAuth 2 session storage key. Skipped.');
28
+ }
29
+ }
30
+
31
+ return undefined;
32
+ },
33
+ save(info) {
34
+ sessionStorage.setItem(sessionKey, JSON.stringify(info));
35
+ },
36
+ };
37
+ }
38
+
39
+ /**
40
+ * Creates an OAuth 2 persistence layer using localStorage.
41
+ */
42
+ export function createOAuth2BrowserPersistence(localKey = '$piral_oauth2_info'): OAuth2Persistence {
43
+ return {
44
+ load() {
45
+ const content = localStorage.getItem(localKey);
46
+
47
+ if (typeof content === 'string') {
48
+ try {
49
+ return JSON.parse(content);
50
+ } catch {
51
+ console.error('Found invalid data in the OAuth 2 local storage key. Skipped.');
52
+ }
53
+ }
54
+
55
+ return undefined;
56
+ },
57
+ save(info) {
58
+ localStorage.setItem(localKey, JSON.stringify(info));
59
+ },
60
+ };
61
+ }