@multiplatform.one/keycloak 1.0.2 → 1.0.4
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/package.json +32 -34
- package/routes/index.d.ts +1 -1
- package/routes/index.js +1 -1
- package/src/Authenticated.tsx +12 -13
- package/src/apollo.ts +1 -1
- package/src/authConfig.ts +2 -0
- package/src/hooks/useTokensFromQuery/index.ts +2 -0
- package/src/hooks/useTokensFromState.ts +2 -2
- package/{lib/@types/expoKeycloakAuth.d.d.mts → src/keycloak/config.ts} +17 -6
- package/src/keycloak/context.ts +3 -1
- package/src/keycloak/index.ts +1 -0
- package/src/keycloak/keycloak.native.ts +49 -15
- package/src/keycloak/keycloak.ts +66 -11
- package/src/provider/AfterAuth.tsx +2 -0
- package/src/provider/AuthProvider/AuthProvider.native.tsx +167 -6
- package/src/provider/AuthProvider/AuthProvider.tsx +66 -43
- package/src/provider/KeycloakProvider.tsx +12 -2
- package/src/routes.ts +19 -0
- package/src/session/session.ts +4 -15
- package/src/token.ts +13 -5
- package/lib/@types/expoKeycloakAuth.d.d.ts +0 -25
- package/lib/@types/expoKeycloakAuth.d.js +0 -1
- package/lib/@types/expoKeycloakAuth.d.mjs +0 -0
- package/lib/Authenticated.d.mts +0 -32
- package/lib/Authenticated.d.ts +0 -32
- package/lib/Authenticated.js +0 -20
- package/lib/Authenticated.mjs +0 -20
- package/lib/apollo.d.mts +0 -7
- package/lib/apollo.d.ts +0 -7
- package/lib/apollo.js +0 -15
- package/lib/apollo.mjs +0 -15
- package/lib/authConfig.d.mts +0 -12
- package/lib/authConfig.d.ts +0 -12
- package/lib/authConfig.js +0 -9
- package/lib/authConfig.mjs +0 -9
- package/lib/chunk-2L5TXBDK.mjs +0 -7
- package/lib/chunk-2MYOFYAK.mjs +0 -53
- package/lib/chunk-2VDDEPLF.js +0 -27
- package/lib/chunk-2VGMJ7WP.js +0 -34
- package/lib/chunk-32AVT4GA.js +0 -9
- package/lib/chunk-3ZTSLDX7.js +0 -13
- package/lib/chunk-6APOOEW6.js +0 -28
- package/lib/chunk-6F4PWJZI.js +0 -1
- package/lib/chunk-6FRYV4MK.mjs +0 -27
- package/lib/chunk-6WBHOY7M.js +0 -50
- package/lib/chunk-74FSREBY.js +0 -161
- package/lib/chunk-A2V2S4HP.js +0 -22
- package/lib/chunk-BUZONXAW.js +0 -1
- package/lib/chunk-C6TOWKPW.mjs +0 -245
- package/lib/chunk-DNEUOHZQ.mjs +0 -28
- package/lib/chunk-EGCPD3BX.js +0 -51
- package/lib/chunk-HH32BME2.mjs +0 -0
- package/lib/chunk-HTWZLBA4.mjs +0 -13
- package/lib/chunk-I5FNGKQ6.js +0 -16
- package/lib/chunk-IGSEESI3.mjs +0 -51
- package/lib/chunk-JAFUI2JQ.mjs +0 -9
- package/lib/chunk-JPQI72HR.mjs +0 -0
- package/lib/chunk-KWNIT4G4.mjs +0 -34
- package/lib/chunk-L3HTVJ4B.mjs +0 -13
- package/lib/chunk-MDAAMGTE.js +0 -1
- package/lib/chunk-NQMFZ6RW.mjs +0 -50
- package/lib/chunk-OAUYHIWG.mjs +0 -0
- package/lib/chunk-OBB23GPX.mjs +0 -0
- package/lib/chunk-OYWXU5N7.mjs +0 -161
- package/lib/chunk-P57PW2II.js +0 -11
- package/lib/chunk-TUJRPTZE.js +0 -13
- package/lib/chunk-UESWAK47.mjs +0 -0
- package/lib/chunk-UKSHA3X3.mjs +0 -16
- package/lib/chunk-VS4M7GMO.js +0 -1
- package/lib/chunk-W4LGQZMF.js +0 -245
- package/lib/chunk-WBQAMGXK.mjs +0 -0
- package/lib/chunk-WMH347OL.mjs +0 -22
- package/lib/chunk-WS4IMGVL.js +0 -1
- package/lib/chunk-YACGLW5Q.js +0 -7
- package/lib/chunk-YYYDFAI4.js +0 -53
- package/lib/chunk-ZCBD6HWK.js +0 -1
- package/lib/chunk-ZFXKCRJC.mjs +0 -11
- package/lib/hooks/index.d.mts +0 -5
- package/lib/hooks/index.d.ts +0 -5
- package/lib/hooks/index.js +0 -19
- package/lib/hooks/index.mjs +0 -19
- package/lib/hooks/useAuthConfig.d.mts +0 -6
- package/lib/hooks/useAuthConfig.d.ts +0 -6
- package/lib/hooks/useAuthConfig.js +0 -8
- package/lib/hooks/useAuthConfig.mjs +0 -8
- package/lib/hooks/useTokensFromQuery/index.d.mts +0 -3
- package/lib/hooks/useTokensFromQuery/index.d.ts +0 -3
- package/lib/hooks/useTokensFromQuery/index.js +0 -7
- package/lib/hooks/useTokensFromQuery/index.mjs +0 -7
- package/lib/hooks/useTokensFromQuery/index.native.d.mts +0 -3
- package/lib/hooks/useTokensFromQuery/index.native.d.ts +0 -3
- package/lib/hooks/useTokensFromQuery/index.native.js +0 -7
- package/lib/hooks/useTokensFromQuery/index.native.mjs +0 -7
- package/lib/hooks/useTokensFromState.d.mts +0 -3
- package/lib/hooks/useTokensFromState.d.ts +0 -3
- package/lib/hooks/useTokensFromState.js +0 -9
- package/lib/hooks/useTokensFromState.mjs +0 -9
- package/lib/index.d.mts +0 -18
- package/lib/index.d.ts +0 -18
- package/lib/index.js +0 -70
- package/lib/index.mjs +0 -70
- package/lib/keycloak/context.d.mts +0 -12
- package/lib/keycloak/context.d.ts +0 -12
- package/lib/keycloak/context.js +0 -7
- package/lib/keycloak/context.mjs +0 -7
- package/lib/keycloak/index.d.mts +0 -7
- package/lib/keycloak/index.d.ts +0 -7
- package/lib/keycloak/index.js +0 -14
- package/lib/keycloak/index.mjs +0 -14
- package/lib/keycloak/keycloak.d.mts +0 -37
- package/lib/keycloak/keycloak.d.ts +0 -37
- package/lib/keycloak/keycloak.js +0 -13
- package/lib/keycloak/keycloak.mjs +0 -13
- package/lib/keycloak/keycloak.native.d.mts +0 -33
- package/lib/keycloak/keycloak.native.d.ts +0 -33
- package/lib/keycloak/keycloak.native.js +0 -134
- package/lib/keycloak/keycloak.native.mjs +0 -134
- package/lib/provider/AfterAuth.d.mts +0 -28
- package/lib/provider/AfterAuth.d.ts +0 -28
- package/lib/provider/AfterAuth.js +0 -18
- package/lib/provider/AfterAuth.mjs +0 -18
- package/lib/provider/AuthProvider/AuthProvider.d.mts +0 -37
- package/lib/provider/AuthProvider/AuthProvider.d.ts +0 -37
- package/lib/provider/AuthProvider/AuthProvider.js +0 -19
- package/lib/provider/AuthProvider/AuthProvider.mjs +0 -19
- package/lib/provider/AuthProvider/AuthProvider.native.d.mts +0 -29
- package/lib/provider/AuthProvider/AuthProvider.native.d.ts +0 -29
- package/lib/provider/AuthProvider/AuthProvider.native.js +0 -25
- package/lib/provider/AuthProvider/AuthProvider.native.mjs +0 -25
- package/lib/provider/AuthProvider/index.d.mts +0 -4
- package/lib/provider/AuthProvider/index.d.ts +0 -4
- package/lib/provider/AuthProvider/index.js +0 -20
- package/lib/provider/AuthProvider/index.mjs +0 -20
- package/lib/provider/KeycloakProvider.d.mts +0 -34
- package/lib/provider/KeycloakProvider.d.ts +0 -34
- package/lib/provider/KeycloakProvider.js +0 -21
- package/lib/provider/KeycloakProvider.mjs +0 -21
- package/lib/provider/index.d.mts +0 -8
- package/lib/provider/index.d.ts +0 -8
- package/lib/provider/index.js +0 -28
- package/lib/provider/index.mjs +0 -28
- package/lib/routes.d.mts +0 -34
- package/lib/routes.d.ts +0 -34
- package/lib/routes.js +0 -131
- package/lib/routes.mjs +0 -131
- package/lib/session/index.d.mts +0 -3
- package/lib/session/index.d.ts +0 -3
- package/lib/session/index.js +0 -10
- package/lib/session/index.mjs +0 -10
- package/lib/session/session.d.mts +0 -18
- package/lib/session/session.d.ts +0 -18
- package/lib/session/session.js +0 -9
- package/lib/session/session.mjs +0 -9
- package/lib/session/session.native.d.mts +0 -8
- package/lib/session/session.native.d.ts +0 -8
- package/lib/session/session.native.js +0 -13
- package/lib/session/session.native.mjs +0 -13
- package/lib/state.d.mts +0 -12
- package/lib/state.d.ts +0 -12
- package/lib/state.js +0 -9
- package/lib/state.mjs +0 -9
- package/lib/token.d.mts +0 -17
- package/lib/token.d.ts +0 -17
- package/lib/token.js +0 -9
- package/lib/token.mjs +0 -9
- package/lib/types.d.mts +0 -20
- package/lib/types.d.ts +0 -20
- package/lib/types.js +0 -1
- package/lib/types.mjs +0 -1
package/package.json
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@multiplatform.one/keycloak",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "keycloak client for multiplatform.one ecosystem",
|
|
5
|
-
"main": "
|
|
6
|
-
"module": "
|
|
5
|
+
"main": "dist/cjs",
|
|
6
|
+
"module": "dist/esm",
|
|
7
|
+
"module:jsx": "dist/jsx",
|
|
7
8
|
"react-native": "src/index.ts",
|
|
8
9
|
"source": "src/index.ts",
|
|
9
|
-
"types": "
|
|
10
|
+
"types": "types/index.d.ts",
|
|
10
11
|
"exports": {
|
|
11
12
|
".": {
|
|
12
|
-
"import": "./
|
|
13
|
-
"require": "./
|
|
14
|
-
"types": "./
|
|
13
|
+
"import": "./dist/esm/index.js",
|
|
14
|
+
"require": "./dist/cjs/index.js",
|
|
15
|
+
"types": "./types/index.d.ts"
|
|
15
16
|
},
|
|
16
17
|
"./routes": {
|
|
17
|
-
"import": "./
|
|
18
|
-
"require": "./
|
|
19
|
-
"types": "./
|
|
18
|
+
"import": "./dist/esm/routes.js",
|
|
19
|
+
"require": "./dist/cjs/routes.js",
|
|
20
|
+
"types": "./dist/esm/routes.d.ts"
|
|
20
21
|
}
|
|
21
22
|
},
|
|
22
23
|
"author": "BitSpur",
|
|
@@ -40,35 +41,31 @@
|
|
|
40
41
|
"url": "https://gitlab.com/risserlabs/multiplatform.one/keycloak"
|
|
41
42
|
},
|
|
42
43
|
"peerDependencies": {
|
|
43
|
-
"@react-native-async-storage/async-storage": "1.
|
|
44
|
-
"
|
|
45
|
-
"multiplatform.one": "^1.0.2",
|
|
44
|
+
"@react-native-async-storage/async-storage": "1.21.0",
|
|
45
|
+
"multiplatform.one": "^1.0.4",
|
|
46
46
|
"next": "^13.0.5",
|
|
47
|
-
"react": "
|
|
48
|
-
"react-native": "
|
|
47
|
+
"react": "18.2.0",
|
|
48
|
+
"react-native": "0.73.6"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@react-native-async-storage/async-storage": "1.21.0",
|
|
52
|
-
"@
|
|
53
|
-
"@types/eslint": "^8.56.
|
|
54
|
-
"@types/jest": "^29.5.
|
|
52
|
+
"@tamagui/build": "^1.95.1",
|
|
53
|
+
"@types/eslint": "^8.56.10",
|
|
54
|
+
"@types/jest": "^29.5.12",
|
|
55
55
|
"@types/lodash.camelcase": "^4.3.9",
|
|
56
56
|
"@types/lodash.startcase": "^4.4.9",
|
|
57
|
-
"@types/react": "^18.
|
|
57
|
+
"@types/react": "^18.3.1",
|
|
58
58
|
"@types/react-native": "^0.73.0",
|
|
59
|
-
"cspell": "^8.
|
|
60
|
-
"eslint": "
|
|
61
|
-
"expo-keycloak-auth": "1.0.14",
|
|
59
|
+
"cspell": "^8.7.0",
|
|
60
|
+
"eslint": "~8.56.0",
|
|
62
61
|
"jest": "^29.7.0",
|
|
63
62
|
"jest-sonar-reporter": "^2.0.0",
|
|
64
|
-
"multiplatform.one": "^1.0.
|
|
65
|
-
"next": "^14.
|
|
66
|
-
"prettier": "^3.2.
|
|
67
|
-
"react": "
|
|
68
|
-
"react-native": "
|
|
69
|
-
"
|
|
70
|
-
"tsup": "^8.0.1",
|
|
71
|
-
"typescript": "^5.3.3"
|
|
63
|
+
"multiplatform.one": "^1.0.4",
|
|
64
|
+
"next": "^14.2.3",
|
|
65
|
+
"prettier": "^3.2.5",
|
|
66
|
+
"react": "18.2.0",
|
|
67
|
+
"react-native": "0.73.6",
|
|
68
|
+
"typescript": "5.3.3"
|
|
72
69
|
},
|
|
73
70
|
"engines": {
|
|
74
71
|
"node": ">=6.0.0"
|
|
@@ -95,9 +92,10 @@
|
|
|
95
92
|
"indent": 4
|
|
96
93
|
},
|
|
97
94
|
"dependencies": {
|
|
98
|
-
"@apollo/client": "^3.
|
|
99
|
-
"expo-auth-session": "^5.
|
|
100
|
-
"
|
|
101
|
-
"
|
|
95
|
+
"@apollo/client": "^3.10.1",
|
|
96
|
+
"expo-auth-session": "^5.5.1",
|
|
97
|
+
"expo-crypto": "^12.8.1",
|
|
98
|
+
"keycloak-js": "^24.0.3",
|
|
99
|
+
"next-auth": "^4.24.7"
|
|
102
100
|
}
|
|
103
101
|
}
|
package/routes/index.d.ts
CHANGED
package/routes/index.js
CHANGED
package/src/Authenticated.tsx
CHANGED
|
@@ -19,11 +19,18 @@
|
|
|
19
19
|
* limitations under the License.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
+
'use client';
|
|
23
|
+
|
|
22
24
|
import React, { ComponentType, PropsWithChildren, useEffect } from 'react';
|
|
23
25
|
import { MultiPlatform } from 'multiplatform.one';
|
|
24
|
-
import {
|
|
26
|
+
import { Text } from 'tamagui';
|
|
27
|
+
import { useAuthConfig, useTokensFromQuery } from './hooks';
|
|
25
28
|
import { useKeycloak } from './keycloak';
|
|
26
29
|
|
|
30
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
31
|
+
|
|
32
|
+
global.AsyncStorage = AsyncStorage;
|
|
33
|
+
|
|
27
34
|
export interface AuthenticatedProps extends PropsWithChildren {
|
|
28
35
|
disabled?: boolean;
|
|
29
36
|
loadingComponent?: ComponentType;
|
|
@@ -34,33 +41,25 @@ export function Authenticated({ children, disabled, loggedOutComponent, loadingC
|
|
|
34
41
|
const authConfig = useAuthConfig();
|
|
35
42
|
const keycloak = useKeycloak();
|
|
36
43
|
const tokensFromQuery = useTokensFromQuery();
|
|
37
|
-
const tokensFromState = useTokensFromState();
|
|
38
44
|
|
|
39
45
|
useEffect(() => {
|
|
40
|
-
if (
|
|
41
|
-
!keycloak ||
|
|
42
|
-
MultiPlatform.isIframe ||
|
|
43
|
-
MultiPlatform.isServer ||
|
|
44
|
-
keycloak.authenticated ||
|
|
45
|
-
tokensFromQuery ||
|
|
46
|
-
tokensFromState
|
|
47
|
-
) {
|
|
46
|
+
if (!keycloak || MultiPlatform.isIframe || MultiPlatform.isServer || keycloak.authenticated || tokensFromQuery) {
|
|
48
47
|
return;
|
|
49
48
|
}
|
|
50
49
|
keycloak.login({
|
|
51
50
|
redirectUri: authConfig.loginRedirectUri,
|
|
52
51
|
});
|
|
53
|
-
}, [keycloak]);
|
|
52
|
+
}, [keycloak?.authenticated]);
|
|
54
53
|
|
|
55
54
|
if (typeof disabled === 'undefined') disabled = authConfig.disabled;
|
|
56
55
|
if (disabled) return <>{children}</>;
|
|
57
56
|
if (typeof keycloak === 'undefined') {
|
|
58
57
|
const LoadingComponent = loadingComponent;
|
|
59
|
-
return LoadingComponent ? <LoadingComponent /> :
|
|
58
|
+
return LoadingComponent ? <LoadingComponent /> : <Text>{authConfig.debug ? 'loading' : null}</Text>;
|
|
60
59
|
}
|
|
61
60
|
if (keycloak.authenticated) return <>{children}</>;
|
|
62
61
|
const LoggedOutComponent = loggedOutComponent;
|
|
63
|
-
return LoggedOutComponent ? <LoggedOutComponent /> :
|
|
62
|
+
return LoggedOutComponent ? <LoggedOutComponent /> : <Text>{authConfig.debug ? 'not authenticated' : null}</Text>;
|
|
64
63
|
}
|
|
65
64
|
|
|
66
65
|
export function withAuthenticated<P extends object>(Component: ComponentType<P>) {
|
package/src/apollo.ts
CHANGED
package/src/authConfig.ts
CHANGED
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { useAuthState } from '../state';
|
|
23
|
-
import {
|
|
23
|
+
import { validOrRefreshableToken } from '../token';
|
|
24
24
|
|
|
25
25
|
export function useTokensFromState() {
|
|
26
26
|
const authState = useAuthState();
|
|
27
|
-
return !!
|
|
27
|
+
return !!validOrRefreshableToken(authState.token, authState.refreshToken);
|
|
28
28
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* File: /src
|
|
2
|
+
* File: /src/keycloak/config.ts
|
|
3
3
|
* Project: @multiplatform.one/keycloak
|
|
4
|
-
* File Created:
|
|
4
|
+
* File Created: 27-04-2024 10:53:19
|
|
5
5
|
* Author: Clay Risser
|
|
6
6
|
* -----
|
|
7
|
-
* BitSpur (c) Copyright 2021 -
|
|
7
|
+
* BitSpur (c) Copyright 2021 - 2024
|
|
8
8
|
*
|
|
9
9
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
10
10
|
* you may not use this file except in compliance with the License.
|
|
@@ -19,7 +19,18 @@
|
|
|
19
19
|
* limitations under the License.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
'use client';
|
|
23
|
+
|
|
24
|
+
import type { KeycloakConfig as KeycloakJsConfig } from 'keycloak-js';
|
|
25
|
+
import { createContext } from 'react';
|
|
26
|
+
|
|
27
|
+
export interface KeycloakConfig extends KeycloakJsConfig {
|
|
28
|
+
publicClientId?: string;
|
|
29
|
+
scopes?: string[];
|
|
25
30
|
}
|
|
31
|
+
|
|
32
|
+
export const KeycloakConfigContext = createContext<KeycloakConfig>({
|
|
33
|
+
clientId: '',
|
|
34
|
+
realm: '',
|
|
35
|
+
url: '',
|
|
36
|
+
});
|
package/src/keycloak/context.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* File: /src/keycloak/context.ts
|
|
3
3
|
* Project: @multiplatform.one/keycloak
|
|
4
|
-
* File Created:
|
|
4
|
+
* File Created: 04-04-2024 15:50:39
|
|
5
5
|
* Author: Clay Risser
|
|
6
6
|
* -----
|
|
7
7
|
* BitSpur (c) Copyright 2021 - 2024
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
* limitations under the License.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
+
'use client';
|
|
23
|
+
|
|
22
24
|
import type { Keycloak } from './keycloak';
|
|
23
25
|
import { createContext } from 'react';
|
|
24
26
|
|
package/src/keycloak/index.ts
CHANGED
|
@@ -21,25 +21,36 @@
|
|
|
21
21
|
|
|
22
22
|
import type KeycloakClient from 'keycloak-js';
|
|
23
23
|
import type { AccessTokenParsed, TokenParsed } from '../token';
|
|
24
|
-
import type {
|
|
24
|
+
import type { KeycloakConfig } from 'keycloak-js';
|
|
25
|
+
import type { KeycloakLoginOptions, KeycloakLogoutOptions } from '../keycloak';
|
|
25
26
|
import type { KeycloakMock } from '../types';
|
|
27
|
+
import { KeycloakConfigContext } from '../keycloak/config';
|
|
28
|
+
import { KeycloakContext } from './context';
|
|
26
29
|
import { MultiPlatform } from 'multiplatform.one';
|
|
27
|
-
import { getSession } from '../session/session';
|
|
28
30
|
import { jwtDecode } from 'jwt-decode';
|
|
29
|
-
import {
|
|
31
|
+
import { useContext } from 'react';
|
|
30
32
|
|
|
31
33
|
export class Keycloak {
|
|
32
|
-
token
|
|
34
|
+
token?: string;
|
|
33
35
|
idToken?: string;
|
|
34
36
|
refreshToken?: string;
|
|
35
37
|
|
|
36
|
-
private _tokenParsed?: AccessTokenParsed;
|
|
37
38
|
private _idTokenParsed?: TokenParsed;
|
|
39
|
+
private _login?: (_options: KeycloakLoginOptions) => Promise<undefined>;
|
|
40
|
+
private _logout?: (_options: KeycloakLogoutOptions) => Promise<undefined>;
|
|
38
41
|
private _refreshTokenParsed?: TokenParsed;
|
|
42
|
+
private _tokenParsed?: AccessTokenParsed;
|
|
39
43
|
private keycloakClient?: KeycloakClient;
|
|
40
44
|
private mock: KeycloakMock | undefined;
|
|
41
45
|
|
|
42
|
-
constructor(
|
|
46
|
+
constructor(
|
|
47
|
+
public readonly config: KeycloakConfig,
|
|
48
|
+
input?: string | KeycloakClient | KeycloakMock,
|
|
49
|
+
idToken?: string,
|
|
50
|
+
refreshToken?: string,
|
|
51
|
+
login?: (_options: KeycloakLoginOptions) => Promise<undefined>,
|
|
52
|
+
logout?: (_options: KeycloakLogoutOptions) => Promise<undefined>,
|
|
53
|
+
) {
|
|
43
54
|
if (typeof input === 'string') {
|
|
44
55
|
this.token = input;
|
|
45
56
|
this.idToken = idToken;
|
|
@@ -56,6 +67,8 @@ export class Keycloak {
|
|
|
56
67
|
if (this.refreshToken && !this._refreshTokenParsed) {
|
|
57
68
|
this._refreshTokenParsed = jwtDecode(this.refreshToken) as TokenParsed;
|
|
58
69
|
}
|
|
70
|
+
this._login = login;
|
|
71
|
+
this._logout = logout;
|
|
59
72
|
}
|
|
60
73
|
|
|
61
74
|
get tokenParsed() {
|
|
@@ -75,7 +88,7 @@ export class Keycloak {
|
|
|
75
88
|
|
|
76
89
|
get clientId() {
|
|
77
90
|
if (this.keycloakClient) return this.keycloakClient.clientId;
|
|
78
|
-
return this.tokenParsed?.azp;
|
|
91
|
+
return this.tokenParsed?.azp || this.config.clientId;
|
|
79
92
|
}
|
|
80
93
|
|
|
81
94
|
get email() {
|
|
@@ -86,7 +99,7 @@ export class Keycloak {
|
|
|
86
99
|
|
|
87
100
|
get realm() {
|
|
88
101
|
if (this.keycloakClient) return this.keycloakClient.realm;
|
|
89
|
-
return this.tokenParsed?.iss?.split('/').pop();
|
|
102
|
+
return this.tokenParsed?.iss?.split('/').pop() || this.config.realm;
|
|
90
103
|
}
|
|
91
104
|
|
|
92
105
|
get realmAccess() {
|
|
@@ -114,35 +127,56 @@ export class Keycloak {
|
|
|
114
127
|
return this.tokenParsed?.preferred_username;
|
|
115
128
|
}
|
|
116
129
|
|
|
130
|
+
async getUserInfo() {
|
|
131
|
+
if (!this.authenticated) return;
|
|
132
|
+
const response = await fetch(`${this.config.url}/realms/${this.config.realm}/protocol/openid-connect/userinfo`, {
|
|
133
|
+
method: 'GET',
|
|
134
|
+
headers: {
|
|
135
|
+
Authorization: 'Bearer ' + this.token,
|
|
136
|
+
Accept: 'application/json',
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
if (response.ok) return response.json();
|
|
140
|
+
}
|
|
141
|
+
|
|
117
142
|
async login(options: KeycloakLoginOptions = {}) {
|
|
143
|
+
this.idToken = undefined;
|
|
144
|
+
this.refreshToken = undefined;
|
|
145
|
+
this.token = undefined;
|
|
118
146
|
if (this.keycloakClient) {
|
|
119
147
|
await this.keycloakClient.login(options);
|
|
148
|
+
} else {
|
|
149
|
+
await this._login?.(options);
|
|
120
150
|
}
|
|
121
151
|
}
|
|
122
152
|
|
|
123
153
|
async logout(options: KeycloakLogoutOptions = {}) {
|
|
154
|
+
this.idToken = undefined;
|
|
155
|
+
this.refreshToken = undefined;
|
|
156
|
+
this.token = undefined;
|
|
124
157
|
if (this.keycloakClient) {
|
|
125
158
|
await this.keycloakClient.logout(options);
|
|
159
|
+
} else {
|
|
160
|
+
await this._logout?.(options);
|
|
126
161
|
}
|
|
127
162
|
}
|
|
128
163
|
|
|
129
|
-
|
|
164
|
+
get authenticated() {
|
|
130
165
|
if (this.mock) return true;
|
|
131
166
|
if (this.keycloakClient) return !!this.keycloakClient.authenticated;
|
|
132
|
-
if (
|
|
133
|
-
if (!this.tokenParsed?.exp) return false;
|
|
167
|
+
if (!this.tokenParsed?.exp || !this.idToken) return false;
|
|
134
168
|
return this.tokenParsed.exp > Date.now() / 1000;
|
|
135
169
|
}
|
|
136
170
|
}
|
|
137
171
|
|
|
138
172
|
export function useKeycloak() {
|
|
139
|
-
const
|
|
140
|
-
|
|
173
|
+
const keycloak = useContext(KeycloakContext);
|
|
174
|
+
const keycloakConfig = useContext(KeycloakConfigContext);
|
|
175
|
+
if (keycloak) return keycloak;
|
|
141
176
|
if (MultiPlatform.isStorybook) {
|
|
142
|
-
return new Keycloak({
|
|
177
|
+
return new Keycloak(keycloakConfig, {
|
|
143
178
|
email: 'storybook@example.com',
|
|
144
179
|
username: 'storybook',
|
|
145
180
|
});
|
|
146
181
|
}
|
|
147
|
-
if (token) return new Keycloak(token, idToken, refreshToken);
|
|
148
182
|
}
|
package/src/keycloak/keycloak.ts
CHANGED
|
@@ -22,16 +22,25 @@
|
|
|
22
22
|
import type KeycloakClient from 'keycloak-js';
|
|
23
23
|
import type { AccessTokenParsed, TokenParsed } from '../token';
|
|
24
24
|
import type { AuthOptions } from 'next-auth';
|
|
25
|
-
import type {
|
|
26
|
-
import type { KeycloakLoginOptions, KeycloakLogoutOptions } from 'keycloak-js';
|
|
25
|
+
import type { AuthRequestPromptOptions } from 'expo-auth-session';
|
|
27
26
|
import type { KeycloakMock } from '../types';
|
|
27
|
+
import type { KeycloakProfile } from 'next-auth/providers/keycloak';
|
|
28
|
+
import type { OAuthConfig } from 'next-auth/providers';
|
|
28
29
|
import type { Session } from '../session/session';
|
|
30
|
+
import { KeycloakConfigContext, type KeycloakConfig } from './config';
|
|
29
31
|
import { KeycloakContext } from './context';
|
|
30
32
|
import { MultiPlatform } from 'multiplatform.one';
|
|
31
33
|
import { getSession, useSession } from '../session/session';
|
|
32
34
|
import { jwtDecode } from 'jwt-decode';
|
|
33
35
|
import { signIn, signOut } from 'next-auth/react';
|
|
34
36
|
import { useContext } from 'react';
|
|
37
|
+
import type {
|
|
38
|
+
KeycloakLoginOptions as KeycloakJsLoginOptions,
|
|
39
|
+
KeycloakLogoutOptions as KeycloakJsLogoutOptions,
|
|
40
|
+
} from 'keycloak-js';
|
|
41
|
+
|
|
42
|
+
export type KeycloakLoginOptions = KeycloakJsLoginOptions & AuthRequestPromptOptions & { redirect?: boolean };
|
|
43
|
+
export type KeycloakLogoutOptions = KeycloakJsLogoutOptions;
|
|
35
44
|
|
|
36
45
|
export class Keycloak {
|
|
37
46
|
token?: string;
|
|
@@ -39,12 +48,21 @@ export class Keycloak {
|
|
|
39
48
|
refreshToken?: string;
|
|
40
49
|
|
|
41
50
|
private _idTokenParsed?: TokenParsed;
|
|
51
|
+
private _login?: (_options: KeycloakLoginOptions) => Promise<undefined>;
|
|
52
|
+
private _logout?: (_options: KeycloakLogoutOptions) => Promise<undefined>;
|
|
42
53
|
private _refreshTokenParsed?: TokenParsed;
|
|
43
54
|
private _tokenParsed?: AccessTokenParsed;
|
|
44
55
|
private keycloakClient?: KeycloakClient;
|
|
45
56
|
private mock: KeycloakMock | undefined;
|
|
46
57
|
|
|
47
|
-
constructor(
|
|
58
|
+
constructor(
|
|
59
|
+
public readonly config: KeycloakConfig,
|
|
60
|
+
input?: string | KeycloakClient | KeycloakMock | Session,
|
|
61
|
+
idToken?: string,
|
|
62
|
+
refreshToken?: string,
|
|
63
|
+
login?: (_options: KeycloakLoginOptions) => Promise<undefined>,
|
|
64
|
+
logout?: (_options: KeycloakLogoutOptions) => Promise<undefined>,
|
|
65
|
+
) {
|
|
48
66
|
if (typeof input === 'string') {
|
|
49
67
|
this.token = input;
|
|
50
68
|
this.idToken = idToken;
|
|
@@ -66,6 +84,8 @@ export class Keycloak {
|
|
|
66
84
|
if (this.refreshToken && !this._refreshTokenParsed) {
|
|
67
85
|
this._refreshTokenParsed = jwtDecode(this.refreshToken) as TokenParsed;
|
|
68
86
|
}
|
|
87
|
+
this._login = login;
|
|
88
|
+
this._logout = logout;
|
|
69
89
|
}
|
|
70
90
|
|
|
71
91
|
get tokenParsed() {
|
|
@@ -85,7 +105,7 @@ export class Keycloak {
|
|
|
85
105
|
|
|
86
106
|
get clientId() {
|
|
87
107
|
if (this.keycloakClient) return this.keycloakClient.clientId;
|
|
88
|
-
return this.tokenParsed?.azp;
|
|
108
|
+
return this.tokenParsed?.azp || this.config.clientId;
|
|
89
109
|
}
|
|
90
110
|
|
|
91
111
|
get email() {
|
|
@@ -96,7 +116,7 @@ export class Keycloak {
|
|
|
96
116
|
|
|
97
117
|
get realm() {
|
|
98
118
|
if (this.keycloakClient) return this.keycloakClient.realm;
|
|
99
|
-
return this.tokenParsed?.iss?.split('/').pop();
|
|
119
|
+
return this.tokenParsed?.iss?.split('/').pop() || this.config.realm;
|
|
100
120
|
}
|
|
101
121
|
|
|
102
122
|
get realmAccess() {
|
|
@@ -124,17 +144,38 @@ export class Keycloak {
|
|
|
124
144
|
return this.tokenParsed?.preferred_username;
|
|
125
145
|
}
|
|
126
146
|
|
|
147
|
+
async getUserInfo() {
|
|
148
|
+
if (!this.authenticated) return;
|
|
149
|
+
const response = await fetch(`${this.config.url}/realms/${this.config.realm}/protocol/openid-connect/userinfo`, {
|
|
150
|
+
method: 'GET',
|
|
151
|
+
headers: {
|
|
152
|
+
Authorization: 'Bearer ' + this.token,
|
|
153
|
+
Accept: 'application/json',
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
if (response.ok) return response.json();
|
|
157
|
+
}
|
|
158
|
+
|
|
127
159
|
async login(options: KeycloakLoginOptions = {}) {
|
|
160
|
+
this.idToken = undefined;
|
|
161
|
+
this.refreshToken = undefined;
|
|
162
|
+
this.token = undefined;
|
|
128
163
|
if (this.keycloakClient) {
|
|
129
164
|
await this.keycloakClient.login(options);
|
|
130
165
|
} else if (MultiPlatform.isNext && !MultiPlatform.isServer) {
|
|
131
166
|
await signIn('keycloak', {
|
|
132
167
|
callbackUrl: options.redirectUri,
|
|
168
|
+
redirect: options.redirect,
|
|
133
169
|
});
|
|
170
|
+
} else {
|
|
171
|
+
await this._login?.(options);
|
|
134
172
|
}
|
|
135
173
|
}
|
|
136
174
|
|
|
137
175
|
async logout(options: KeycloakLogoutOptions = {}) {
|
|
176
|
+
this.idToken = undefined;
|
|
177
|
+
this.refreshToken = undefined;
|
|
178
|
+
this.token = undefined;
|
|
138
179
|
if (this.keycloakClient) {
|
|
139
180
|
await this.keycloakClient.logout(options);
|
|
140
181
|
} else if (MultiPlatform.isNext && !MultiPlatform.isServer) {
|
|
@@ -142,6 +183,8 @@ export class Keycloak {
|
|
|
142
183
|
await signOut({
|
|
143
184
|
callbackUrl: options.redirectUri,
|
|
144
185
|
});
|
|
186
|
+
} else {
|
|
187
|
+
await this._logout?.(options);
|
|
145
188
|
}
|
|
146
189
|
}
|
|
147
190
|
|
|
@@ -155,22 +198,34 @@ export class Keycloak {
|
|
|
155
198
|
|
|
156
199
|
export function useKeycloak() {
|
|
157
200
|
const keycloak = useContext(KeycloakContext);
|
|
201
|
+
const keycloakConfig = useContext(KeycloakConfigContext);
|
|
158
202
|
if (keycloak) return keycloak;
|
|
159
203
|
const { session, status } = useSession();
|
|
160
204
|
if (MultiPlatform.isStorybook) {
|
|
161
|
-
return new Keycloak({
|
|
205
|
+
return new Keycloak(keycloakConfig, {
|
|
162
206
|
email: 'storybook@example.com',
|
|
163
207
|
username: 'storybook',
|
|
164
208
|
});
|
|
165
209
|
} else if (status === 'unauthenticated') {
|
|
166
|
-
return new Keycloak();
|
|
210
|
+
return new Keycloak(keycloakConfig);
|
|
167
211
|
} else if (status === 'authenticated' && session?.accessToken) {
|
|
168
|
-
return new Keycloak(session as Session);
|
|
212
|
+
return new Keycloak(keycloakConfig, session as Session);
|
|
169
213
|
}
|
|
170
214
|
}
|
|
171
215
|
|
|
172
|
-
export async function getKeycloak(options?: AuthOptions
|
|
216
|
+
export async function getKeycloak(options?: AuthOptions, req?: any, res?: any) {
|
|
217
|
+
const keycloakProvider: OAuthConfig<KeycloakProfile> | undefined = options?.providers.find(
|
|
218
|
+
(provider) => provider.id === 'keycloak',
|
|
219
|
+
)?.options;
|
|
220
|
+
const parts = keycloakProvider?.issuer?.split('/realms/');
|
|
221
|
+
const keycloakConfig: KeycloakConfig = {
|
|
222
|
+
realm: parts?.[1]?.split('/')[0] || '',
|
|
223
|
+
url: parts?.[0] || '',
|
|
224
|
+
clientId: keycloakProvider?.clientId || '',
|
|
225
|
+
};
|
|
173
226
|
const session = await getSession(options, req, res);
|
|
174
|
-
if (session?.accessToken)
|
|
175
|
-
|
|
227
|
+
if (session?.accessToken) {
|
|
228
|
+
return new Keycloak(keycloakConfig, session as Session);
|
|
229
|
+
}
|
|
230
|
+
return new Keycloak(keycloakConfig);
|
|
176
231
|
}
|