@multiplatform.one/keycloak 0.2.17 → 0.3.3

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.
Files changed (40) hide show
  1. package/dist/esm/authConfig.mjs +2 -2
  2. package/dist/esm/authConfig.mjs.map +1 -1
  3. package/dist/esm/provider/afterAuth.mjs +1 -1
  4. package/dist/esm/provider/afterAuth.mjs.map +2 -2
  5. package/dist/esm/provider/index.mjs +64 -8
  6. package/dist/esm/provider/index.mjs.map +2 -2
  7. package/dist/esm/provider/keycloakProvider.mjs +54 -31
  8. package/dist/esm/provider/keycloakProvider.mjs.map +2 -2
  9. package/dist/esm/provider/keycloakProvider.native.mjs +1 -1
  10. package/dist/esm/provider/keycloakProvider.native.mjs.map +1 -1
  11. package/dist/jsx/authConfig.mjs +2 -2
  12. package/dist/jsx/authConfig.mjs.map +1 -1
  13. package/dist/jsx/provider/afterAuth.mjs +1 -1
  14. package/dist/jsx/provider/afterAuth.mjs.map +2 -2
  15. package/dist/jsx/provider/index.mjs +45 -7
  16. package/dist/jsx/provider/index.mjs.map +2 -2
  17. package/dist/jsx/provider/keycloakProvider.mjs +61 -32
  18. package/dist/jsx/provider/keycloakProvider.mjs.map +2 -2
  19. package/dist/jsx/provider/keycloakProvider.native.mjs +1 -1
  20. package/dist/jsx/provider/keycloakProvider.native.mjs.map +1 -1
  21. package/dist/lib/authConfig.d.ts +1 -1
  22. package/dist/lib/authConfig.js +2 -2
  23. package/dist/lib/authConfig.js.map +1 -1
  24. package/dist/lib/provider/afterAuth.js +1 -1
  25. package/dist/lib/provider/afterAuth.js.map +2 -2
  26. package/dist/lib/provider/index.d.ts +11 -4
  27. package/dist/lib/provider/index.js +77 -7
  28. package/dist/lib/provider/index.js.map +2 -2
  29. package/dist/lib/provider/keycloakProvider.d.ts +2 -10
  30. package/dist/lib/provider/keycloakProvider.js +62 -32
  31. package/dist/lib/provider/keycloakProvider.js.map +2 -2
  32. package/dist/lib/provider/keycloakProvider.native.js +1 -1
  33. package/dist/lib/provider/keycloakProvider.native.js.map +1 -1
  34. package/dist/lib/tsconfig.build.tsbuildinfo +1 -1
  35. package/package.json +1 -1
  36. package/src/authConfig.ts +3 -3
  37. package/src/provider/afterAuth.tsx +1 -1
  38. package/src/provider/index.tsx +65 -12
  39. package/src/provider/keycloakProvider.native.tsx +1 -1
  40. package/src/provider/keycloakProvider.tsx +64 -43
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multiplatform.one/keycloak",
3
- "version": "0.2.17",
3
+ "version": "0.3.3",
4
4
  "description": "keycloak client for multiplatform.one ecosystem",
5
5
  "main": "dist/lib",
6
6
  "module": "dist/esm",
package/src/authConfig.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  * File Created: 08-11-2022 09:21:15
5
5
  * Author: Clay Risser
6
6
  * -----
7
- * Last Modified: 25-04-2023 23:08:17
7
+ * Last Modified: 14-05-2023 01:22:15
8
8
  * Modified By: Clay Risser
9
9
  * -----
10
10
  * Risser Labs LLC (c) Copyright 2021 - 2022
@@ -26,8 +26,8 @@ import { createContext } from 'react';
26
26
 
27
27
  export const defaultAuthConfig: AuthConfig = {
28
28
  ensureFreshness: true,
29
- persist: true,
30
- ssr: true,
29
+ persist: false,
30
+ ssr: false,
31
31
  };
32
32
 
33
33
  export const AuthConfigContext = createContext<AuthConfig>(defaultAuthConfig);
@@ -57,7 +57,7 @@ export const AfterAuth: FC<AfterAuthProps> = ({ children }: AfterAuthProps) => {
57
57
  }, [authState.idToken]);
58
58
 
59
59
  useEffect(() => {
60
- if (authConfig.debug && authState.refreshToken) logger.debug(authState.refreshToken);
60
+ if (authConfig.debug && authState.refreshToken) logger.debug('refreshToken', authState.refreshToken);
61
61
  }, [authState.refreshToken]);
62
62
 
63
63
  useEffect(() => {
@@ -23,30 +23,83 @@
23
23
  */
24
24
 
25
25
  import React, { useMemo } from 'react';
26
+ import type { AuthClientEvent, AuthClientError, AuthClientTokens } from '@bitspur/react-keycloak-core';
26
27
  import type { AuthConfig } from '../authConfig';
27
- import type { FC } from 'react';
28
- import type { KeycloakProviderProps } from './keycloakProvider';
28
+ import type { FC, ComponentType, ReactNode } from 'react';
29
+ import type { KeycloakConfig, KeycloakInitOptions } from '@bitspur/keycloak-js';
29
30
  import { AuthConfigContext, defaultAuthConfig } from '../authConfig';
30
31
  import { KeycloakProvider } from './keycloakProvider';
32
+ // @ts-ignore
33
+ import { config } from 'app/config';
31
34
 
32
- export interface AuthProviderProps extends KeycloakProviderProps {
33
- authConfig?: Partial<AuthConfig>;
35
+ export interface AuthProviderProps
36
+ extends Partial<Omit<AuthConfig, 'persist'>>,
37
+ Omit<KeycloakConfig, 'url'>,
38
+ KeycloakInitOptions {
39
+ children: ReactNode;
40
+ cookies?: unknown;
41
+ disabled?: boolean;
42
+ baseUrl?: string;
43
+ loadingComponent?: ComponentType;
44
+ onEvent?: (eventType: AuthClientEvent, error?: AuthClientError) => void;
45
+ onTokens?: (tokens: AuthClientTokens) => void;
34
46
  }
35
47
 
36
- export const AuthProvider: FC<AuthProviderProps> = (props: AuthProviderProps) => {
37
- const { authConfig, debug } = props;
38
- const authConfigValue = useMemo(
48
+ const persist = config.get('KEYCLOAK_PERSIST') === '1';
49
+
50
+ export const AuthProvider: FC<AuthProviderProps> = ({
51
+ // keycloak config
52
+ realm,
53
+ clientId,
54
+ baseUrl,
55
+ // auth config
56
+ debug,
57
+ ensureFreshness,
58
+ loginRoute,
59
+ messageHandlerKeys,
60
+ ssr,
61
+ // props
62
+ children,
63
+ cookies,
64
+ disabled,
65
+ loadingComponent,
66
+ onEvent,
67
+ onTokens,
68
+ ...keycloakInitOptions
69
+ }: AuthProviderProps) => {
70
+ const authConfig = useMemo(
39
71
  () => ({
40
72
  ...defaultAuthConfig,
41
- ...authConfig,
42
- debug: !!debug,
73
+ debug,
74
+ ensureFreshness,
75
+ loginRoute,
76
+ messageHandlerKeys,
77
+ persist: ssr ? false : persist,
78
+ ssr,
43
79
  }),
44
- [],
80
+ [debug, ensureFreshness, loginRoute, messageHandlerKeys, persist, ssr],
45
81
  );
46
82
 
83
+ if (disabled) {
84
+ return <AuthConfigContext.Provider value={authConfig}>{children}</AuthConfigContext.Provider>;
85
+ }
86
+
47
87
  return (
48
- <AuthConfigContext.Provider value={authConfigValue}>
49
- <KeycloakProvider {...props} />
88
+ <AuthConfigContext.Provider value={authConfig}>
89
+ <KeycloakProvider
90
+ cookies={cookies}
91
+ keycloakConfig={{
92
+ realm,
93
+ clientId,
94
+ url: baseUrl,
95
+ }}
96
+ keycloakInitOptions={keycloakInitOptions}
97
+ loadingComponent={loadingComponent}
98
+ onEvent={onEvent}
99
+ onTokens={onTokens}
100
+ >
101
+ {children}
102
+ </KeycloakProvider>
50
103
  </AuthConfigContext.Provider>
51
104
  );
52
105
  };
@@ -32,7 +32,7 @@ import { KeycloakProvider as ExpoKeycloakProvider } from 'expo-keycloak-auth';
32
32
  export const KeycloakProvider: FC<KeycloakProviderProps> = ({ children, keycloakConfig }: KeycloakProviderProps) => {
33
33
  const clonedKeycloakConfig = {
34
34
  ...(keycloakConfig as Omit<KeycloakConfig, 'baseUrl'>),
35
- url: keycloakConfig.baseUrl,
35
+ url: keycloakConfig.url,
36
36
  };
37
37
  // @ts-ignore
38
38
  delete keycloakConfig.baseUrl;
@@ -25,7 +25,7 @@
25
25
  import Keycloak from '@bitspur/keycloak-js';
26
26
  import React, { useMemo, useEffect, useState, useCallback } from 'react';
27
27
  import type { AuthClientEvent, AuthClientError, AuthClientTokens } from '@bitspur/react-keycloak-core';
28
- import type { KeycloakInitOptions } from '@bitspur/keycloak-js';
28
+ import type { KeycloakInitOptions, KeycloakConfig } from '@bitspur/keycloak-js';
29
29
  import type { ReactNode, ComponentType } from 'react';
30
30
  import { AfterAuth } from './afterAuth';
31
31
  import { MultiPlatform } from 'multiplatform.one';
@@ -38,8 +38,6 @@ import { useRouter } from 'next/router';
38
38
  export interface KeycloakProviderProps {
39
39
  children: ReactNode;
40
40
  cookies?: unknown;
41
- debug?: boolean;
42
- disabled?: boolean;
43
41
  keycloakConfig: KeycloakConfig;
44
42
  keycloakInitOptions?: KeycloakInitOptions;
45
43
  loadingComponent?: ComponentType;
@@ -52,35 +50,42 @@ const logger = console;
52
50
  export function KeycloakProvider({
53
51
  children,
54
52
  cookies,
55
- debug,
56
53
  keycloakConfig,
57
54
  keycloakInitOptions,
58
55
  loadingComponent,
59
56
  onEvent,
60
57
  onTokens,
61
58
  }: KeycloakProviderProps) {
59
+ const { debug, ensureFreshness, persist, messageHandlerKeys, ssr } = useAuthConfig();
62
60
  const authState = useAuthState();
63
61
  const LoadingComponent = loadingComponent || (() => <>{debug ? 'authenticating' : null}</>);
64
62
  const { query } = MultiPlatform.isNext ? useRouter() : { query: {} };
65
- const authConfig = useAuthConfig();
63
+ const [refreshToken, setRefreshToken] = useState<string | boolean>(
64
+ validToken(
65
+ (ensureFreshness &&
66
+ (('refreshToken' in query && (query.refreshToken?.toString() || true)) ||
67
+ (persist && authState.refreshToken))) ||
68
+ false,
69
+ ),
70
+ );
66
71
  const [idToken, setIdToken] = useState<string | boolean>(
67
- ('idToken' in query && (query.idToken?.toString() || true)) || (authConfig.persist && authState.idToken) || false,
72
+ validToken(
73
+ ('idToken' in query && (query.idToken?.toString() || true)) || (persist && authState.idToken) || false,
74
+ refreshToken,
75
+ ),
68
76
  );
69
77
  const [token, setToken] = useState<string | boolean>(
70
- ('token' in query && (query.token?.toString() || true)) || (authConfig.persist && authState.token) || false,
71
- );
72
- const [refreshToken, setRefreshToken] = useState<string | boolean>(
73
- (authConfig.ensureFreshness &&
74
- (('refreshToken' in query && (query.refreshToken?.toString() || true)) ||
75
- (authConfig.persist && authState.refreshToken))) ||
76
- false,
78
+ validToken(
79
+ ('token' in query && (query.token?.toString() || true)) || (persist && authState.token) || false,
80
+ refreshToken,
81
+ ),
77
82
  );
78
83
  const explicitToken = 'idToken' in query || 'token' in query || 'refreshToken' in query;
79
84
 
80
85
  useEffect(() => {
81
86
  if (token !== true && refreshToken !== true && idToken !== true) return;
82
87
  if (debug) logger.debug('post message', { type: 'LOADED' });
83
- (authConfig.messageHandlerKeys || []).forEach((key: string) => {
88
+ (messageHandlerKeys || []).forEach((key: string) => {
84
89
  window?.webkit?.messageHandlers?.[key]?.postMessage(JSON.stringify({ type: 'LOADED' }));
85
90
  });
86
91
  window?.ReactNativeWebView?.postMessage(JSON.stringify({ type: 'LOADED' }));
@@ -88,9 +93,9 @@ export function KeycloakProvider({
88
93
  }, []);
89
94
 
90
95
  useEffect(() => {
91
- if (token !== true) return;
96
+ if (refreshToken !== true) return;
92
97
  const messageCallback = (e: MessageEvent<any>) => {
93
- let data = e?.data;
98
+ let data = e?.data || null;
94
99
  if (typeof data === 'string') {
95
100
  try {
96
101
  data = JSON.parse(data);
@@ -99,15 +104,15 @@ export function KeycloakProvider({
99
104
  if (debug) logger.debug('received message', data);
100
105
  const message: MessageSchema = data;
101
106
  if (!message.type) return;
102
- if (message.type.toUpperCase() === 'TOKEN' && message.payload) {
103
- if (debug) logger.debug('setting token', message.payload);
104
- setToken(message.payload);
107
+ if (message.type.toUpperCase() === 'REFRESH_TOKEN' && message.payload) {
108
+ if (debug) logger.debug('setting refresh token', message.payload);
109
+ setRefreshToken(validToken(message.payload));
105
110
  }
106
111
  };
107
112
  if (debug) {
108
113
  logger.debug('listening for message', {
109
- type: 'TOKEN',
110
- payload: '<some_token>',
114
+ type: 'REFRESH_TOKEN',
115
+ payload: '<some_refresh_token>',
111
116
  });
112
117
  }
113
118
  window.addEventListener('message', messageCallback);
@@ -117,9 +122,9 @@ export function KeycloakProvider({
117
122
  }, []);
118
123
 
119
124
  useEffect(() => {
120
- if (idToken !== true) return;
125
+ if (token !== true) return;
121
126
  const messageCallback = (e: MessageEvent<any>) => {
122
- let data = e?.data;
127
+ let data = e?.data || null;
123
128
  if (typeof data === 'string') {
124
129
  try {
125
130
  data = JSON.parse(data);
@@ -128,15 +133,15 @@ export function KeycloakProvider({
128
133
  if (debug) logger.debug('received message', data);
129
134
  const message: MessageSchema = data;
130
135
  if (!message.type) return;
131
- if (message.type.toUpperCase() === 'ID_TOKEN' && message.payload) {
132
- if (debug) logger.debug('setting id token', message.payload);
133
- setIdToken(message.payload);
136
+ if (message.type.toUpperCase() === 'TOKEN' && message.payload) {
137
+ if (debug) logger.debug('setting token', message.payload);
138
+ setToken(validToken(message.payload, refreshToken));
134
139
  }
135
140
  };
136
141
  if (debug) {
137
142
  logger.debug('listening for message', {
138
- type: 'ID_TOKEN',
139
- payload: '<some_id_token>',
143
+ type: 'TOKEN',
144
+ payload: '<some_token>',
140
145
  });
141
146
  }
142
147
  window.addEventListener('message', messageCallback);
@@ -146,9 +151,9 @@ export function KeycloakProvider({
146
151
  }, []);
147
152
 
148
153
  useEffect(() => {
149
- if (refreshToken !== true) return;
154
+ if (idToken !== true) return;
150
155
  const messageCallback = (e: MessageEvent<any>) => {
151
- let data = e?.data;
156
+ let data = e?.data || null;
152
157
  if (typeof data === 'string') {
153
158
  try {
154
159
  data = JSON.parse(data);
@@ -157,15 +162,15 @@ export function KeycloakProvider({
157
162
  if (debug) logger.debug('received message', data);
158
163
  const message: MessageSchema = data;
159
164
  if (!message.type) return;
160
- if (message.type.toUpperCase() === 'REFRESH_TOKEN' && message.payload) {
161
- if (debug) logger.debug('setting refresh token', message.payload);
162
- setRefreshToken(message.payload);
165
+ if (message.type.toUpperCase() === 'ID_TOKEN' && message.payload) {
166
+ if (debug) logger.debug('setting id token', message.payload);
167
+ setIdToken(validToken(message.payload, refreshToken));
163
168
  }
164
169
  };
165
170
  if (debug) {
166
171
  logger.debug('listening for message', {
167
- type: 'REFRESH_TOKEN',
168
- payload: '<some_refresh_token>',
172
+ type: 'ID_TOKEN',
173
+ payload: '<some_id_token>',
169
174
  });
170
175
  }
171
176
  window.addEventListener('message', messageCallback);
@@ -188,7 +193,7 @@ export function KeycloakProvider({
188
193
  () =>
189
194
  typeof window !== 'undefined' &&
190
195
  new Keycloak({
191
- url: keycloakConfig.baseUrl,
196
+ url: keycloakConfig.url,
192
197
  realm: keycloakConfig.realm,
193
198
  clientId: keycloakConfig.clientId,
194
199
  }),
@@ -214,11 +219,13 @@ export function KeycloakProvider({
214
219
  }, [keycloakInitOptions, token, refreshToken, idToken]);
215
220
 
216
221
  if (token === true || idToken === true || refreshToken === true) return <LoadingComponent />;
217
- if (cookies && authConfig.ssr) {
222
+ if (cookies && ssr) {
218
223
  return (
219
224
  // @ts-ignore
220
225
  <SSRKeycloakProvider
221
226
  LoadingComponent={<LoadingComponent />}
227
+ // keycloak-js already handles refreshing tokens
228
+ autoRefreshToken={false}
222
229
  initOptions={initOptions}
223
230
  onEvent={handleEvent}
224
231
  onTokens={onTokens}
@@ -226,7 +233,7 @@ export function KeycloakProvider({
226
233
  keycloakConfig={{
227
234
  clientId: keycloakConfig.clientId,
228
235
  realm: keycloakConfig.realm,
229
- url: keycloakConfig.baseUrl,
236
+ url: keycloakConfig.url,
230
237
  }}
231
238
  >
232
239
  <AfterAuth>{children}</AfterAuth>
@@ -238,6 +245,8 @@ export function KeycloakProvider({
238
245
  <ReactKeycloakProvider
239
246
  LoadingComponent={<LoadingComponent />}
240
247
  authClient={keycloak}
248
+ // keycloak-js already handles refreshing tokens
249
+ autoRefreshToken={false}
241
250
  initOptions={initOptions}
242
251
  onEvent={handleEvent}
243
252
  onTokens={onTokens}
@@ -247,11 +256,23 @@ export function KeycloakProvider({
247
256
  );
248
257
  }
249
258
 
250
- export interface KeycloakConfig {
251
- clientId: string;
252
- realm: string;
253
- scheme?: string;
254
- baseUrl: string;
259
+ function validToken(token: string | boolean, refreshToken?: string | boolean) {
260
+ if (typeof token !== 'string') return token;
261
+ if (refreshToken) {
262
+ if (refreshToken === true || isTokenValid(refreshToken)) return token;
263
+ return false;
264
+ }
265
+ return isTokenValid(token) ? token : false;
266
+ }
267
+
268
+ function isTokenValid(token: string) {
269
+ try {
270
+ const { exp } = JSON.parse(Buffer.from(token.split('.')?.[1] || '', 'base64').toString() || '{}');
271
+ return Math.floor(Date.now() / 1000) <= exp;
272
+ } catch (err) {
273
+ if (err instanceof SyntaxError) return false;
274
+ throw err;
275
+ }
255
276
  }
256
277
 
257
278
  export const defaultKeycloakInitOptions: KeycloakInitOptions = {