@multiplatform.one/keycloak 0.0.1 → 0.0.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 (116) hide show
  1. package/esm/Authenticated.js +8 -19
  2. package/esm/Authenticated.js.map +1 -1
  3. package/esm/authConfig.js +2 -2
  4. package/esm/authConfig.js.map +1 -1
  5. package/esm/expo/ExpoKeycloakContext.js +28 -0
  6. package/esm/expo/ExpoKeycloakContext.js.map +1 -0
  7. package/esm/expo/ExpoKeycloakProvider.js +113 -0
  8. package/esm/expo/ExpoKeycloakProvider.js.map +1 -0
  9. package/esm/expo/KeycloakContext.js +28 -0
  10. package/esm/expo/KeycloakContext.js.map +1 -0
  11. package/esm/expo/KeycloakProvider.js +142 -0
  12. package/esm/expo/KeycloakProvider.js.map +1 -0
  13. package/esm/expo/const.js +42 -0
  14. package/esm/expo/const.js.map +1 -0
  15. package/esm/expo/helpers.js +56 -0
  16. package/esm/expo/helpers.js.map +1 -0
  17. package/esm/expo/index.js +28 -0
  18. package/esm/expo/index.js.map +1 -0
  19. package/esm/expo/useExpoKeycloak.js +44 -0
  20. package/esm/expo/useExpoKeycloak.js.map +1 -0
  21. package/esm/expo/useTokenStorage.js +153 -0
  22. package/esm/expo/useTokenStorage.js.map +1 -0
  23. package/esm/hooks/useCurrentRouteName.js +2 -3
  24. package/esm/hooks/useCurrentRouteName.js.map +1 -1
  25. package/esm/hooks/useKeycloak/index.js +1 -1
  26. package/esm/hooks/useKeycloak/index.js.map +1 -1
  27. package/esm/hooks/useKeycloak/useKeycloak.js +17 -24
  28. package/esm/hooks/useKeycloak/useKeycloak.js.map +1 -1
  29. package/esm/hooks/useKeycloak/useKeycloak.native.js +60 -19
  30. package/esm/hooks/useKeycloak/useKeycloak.native.js.map +1 -1
  31. package/esm/hooks/useLogin.js +12 -30
  32. package/esm/hooks/useLogin.js.map +1 -1
  33. package/esm/provider/afterAuth.js +21 -39
  34. package/esm/provider/afterAuth.js.map +1 -1
  35. package/esm/provider/index.js +11 -28
  36. package/esm/provider/index.js.map +1 -1
  37. package/esm/provider/keycloakProvider.js +55 -100
  38. package/esm/provider/keycloakProvider.js.map +1 -1
  39. package/esm/provider/keycloakProvider.native.js +12 -11
  40. package/esm/provider/keycloakProvider.native.js.map +1 -1
  41. package/esm/state/index.js +2 -2
  42. package/esm/state/index.js.map +1 -1
  43. package/lib/Authenticated.js +38 -26
  44. package/lib/Authenticated.js.map +1 -1
  45. package/lib/authConfig.js +2 -4
  46. package/lib/authConfig.js.map +1 -1
  47. package/lib/expo/ExpoKeycloakContext.d.ts +32 -0
  48. package/lib/expo/ExpoKeycloakContext.js +47 -0
  49. package/lib/expo/ExpoKeycloakContext.js.map +1 -0
  50. package/lib/expo/ExpoKeycloakProvider.d.ts +37 -0
  51. package/lib/expo/ExpoKeycloakProvider.js +130 -0
  52. package/lib/expo/ExpoKeycloakProvider.js.map +1 -0
  53. package/lib/expo/KeycloakContext.d.ts +32 -0
  54. package/lib/expo/KeycloakContext.js +49 -0
  55. package/lib/expo/KeycloakContext.js.map +1 -0
  56. package/lib/expo/KeycloakProvider.d.ts +37 -0
  57. package/lib/expo/KeycloakProvider.js +138 -0
  58. package/lib/expo/KeycloakProvider.js.map +1 -0
  59. package/lib/expo/const.d.ts +33 -0
  60. package/lib/expo/const.js +66 -0
  61. package/lib/expo/const.js.map +1 -0
  62. package/lib/expo/helpers.d.ts +41 -0
  63. package/lib/expo/helpers.js +77 -0
  64. package/lib/expo/helpers.js.map +1 -0
  65. package/lib/expo/index.d.ts +26 -0
  66. package/lib/expo/index.js +50 -0
  67. package/lib/expo/index.js.map +1 -0
  68. package/lib/expo/useExpoKeycloak.d.ts +31 -0
  69. package/lib/expo/useExpoKeycloak.js +62 -0
  70. package/lib/expo/useExpoKeycloak.js.map +1 -0
  71. package/lib/expo/useTokenStorage.d.ts +36 -0
  72. package/lib/expo/useTokenStorage.js +166 -0
  73. package/lib/expo/useTokenStorage.js.map +1 -0
  74. package/lib/hooks/index.js +3 -9
  75. package/lib/hooks/index.js.map +1 -1
  76. package/lib/hooks/useAuthConfig.js +0 -2
  77. package/lib/hooks/useAuthConfig.js.map +1 -1
  78. package/lib/hooks/useCurrentRouteName.js +6 -8
  79. package/lib/hooks/useCurrentRouteName.js.map +1 -1
  80. package/lib/hooks/useKeycloak/index.d.ts +7 -4
  81. package/lib/hooks/useKeycloak/index.js +1 -7
  82. package/lib/hooks/useKeycloak/index.js.map +1 -1
  83. package/lib/hooks/useKeycloak/useKeycloak.js +45 -30
  84. package/lib/hooks/useKeycloak/useKeycloak.js.map +1 -1
  85. package/lib/hooks/useKeycloak/useKeycloak.native.d.ts +13 -3
  86. package/lib/hooks/useKeycloak/useKeycloak.native.js +61 -25
  87. package/lib/hooks/useKeycloak/useKeycloak.native.js.map +1 -1
  88. package/lib/hooks/useLogin.js +16 -25
  89. package/lib/hooks/useLogin.js.map +1 -1
  90. package/lib/index.js +4 -10
  91. package/lib/index.js.map +1 -1
  92. package/lib/provider/afterAuth.js +52 -52
  93. package/lib/provider/afterAuth.js.map +1 -1
  94. package/lib/provider/index.js +44 -43
  95. package/lib/provider/index.js.map +1 -1
  96. package/lib/provider/keycloakProvider.js +93 -114
  97. package/lib/provider/keycloakProvider.js.map +1 -1
  98. package/lib/provider/keycloakProvider.native.js +39 -15
  99. package/lib/provider/keycloakProvider.native.js.map +1 -1
  100. package/lib/state/index.js +3 -5
  101. package/lib/state/index.js.map +1 -1
  102. package/lib/tsconfig.build.tsbuildinfo +1 -1
  103. package/lib/types.js +2 -4
  104. package/lib/types.js.map +1 -1
  105. package/package.json +5 -4
  106. package/src/expo/ExpoKeycloakContext.ts +41 -0
  107. package/src/expo/ExpoKeycloakProvider.tsx +139 -0
  108. package/src/expo/NOTES +1 -0
  109. package/src/expo/const.ts +42 -0
  110. package/src/expo/helpers.ts +74 -0
  111. package/src/{@types/expoKeycloakAuth.d.ts → expo/index.ts} +6 -7
  112. package/src/expo/useExpoKeycloak.ts +44 -0
  113. package/src/expo/useTokenStorage.ts +168 -0
  114. package/src/hooks/useKeycloak/index.ts +11 -4
  115. package/src/hooks/useKeycloak/useKeycloak.native.ts +72 -8
  116. package/src/provider/keycloakProvider.native.tsx +8 -4
package/lib/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":[],"sources":["../src/types.ts"],"sourcesContent":["/**\n * File: /src/types.ts\n * Project: @multiplatform.one/keycloak\n * File Created: 25-11-2022 10:27:53\n * Author: Clay Risser\n * -----\n * Last Modified: 25-11-2022 10:28:25\n * Modified By: Clay Risser\n * -----\n * Risser Labs LLC (c) Copyright 2022\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\ndeclare global {\n interface Window {\n ReactNativeWebView: {\n postMessage: (body: string) => unknown;\n };\n webkit: {\n messageHandlers: {\n [key: string]: {\n postMessage: (body: string) => unknown;\n };\n };\n };\n }\n}\n\nexport default null;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EAtBA,eAuCe,IAAI;EAAA;AAAA"}
1
+ {"version":3,"file":"types.js","names":[],"sources":["../src/types.ts"],"sourcesContent":["/**\n * File: /src/types.ts\n * Project: @multiplatform.one/keycloak\n * File Created: 25-11-2022 10:27:53\n * Author: Clay Risser\n * -----\n * Last Modified: 25-11-2022 10:28:25\n * Modified By: Clay Risser\n * -----\n * Risser Labs LLC (c) Copyright 2022\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\ndeclare global {\n interface Window {\n ReactNativeWebView: {\n postMessage: (body: string) => unknown;\n };\n webkit: {\n messageHandlers: {\n [key: string]: {\n postMessage: (body: string) => unknown;\n };\n };\n };\n }\n}\n\nexport default null;\n"],"mappings":";;;;;;;;;;;;;;;;;;;EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EAtBA,eAuCe,IAAI;EAAA;AAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multiplatform.one/keycloak",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "keycloak client for multiplatform.one ecosystem",
5
5
  "main": "lib/index.js",
6
6
  "module": "esm/index.js",
@@ -88,7 +88,7 @@
88
88
  "babel-plugin-transform-typescript-metadata": "^0.3.2",
89
89
  "browserlist": "^1.0.1",
90
90
  "core-js": "^3.26.1",
91
- "cspell": "^6.14.3",
91
+ "cspell": "^6.15.0",
92
92
  "dripsy": "^3.8.1",
93
93
  "eslint": "^8.28.0",
94
94
  "eslint-config-alloy": "^4.7.0",
@@ -97,7 +97,7 @@
97
97
  "jest": "^29.3.1",
98
98
  "jest-sonar-reporter": "^2.0.0",
99
99
  "lockfile-lint": "^4.9.6",
100
- "multiplatform.one": "^0.0.1",
100
+ "multiplatform.one": "^0.0.2",
101
101
  "next": "^13.0.5",
102
102
  "prettier": "^2.8.0",
103
103
  "react": "^18.2.0",
@@ -135,7 +135,8 @@
135
135
  "dependencies": {
136
136
  "@react-keycloak/ssr": "^3.3.0",
137
137
  "@react-keycloak/web": "^3.4.0",
138
- "expo-keycloak-auth": "^1.0.14",
138
+ "@react-native-async-storage/async-storage": "^1.17.11",
139
+ "expo-auth-session": "^3.7.2",
139
140
  "keycloak-js": "^20.0.1"
140
141
  }
141
142
  }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * File: /src/expo/ExpoKeycloakContext.ts
3
+ * Project: @multiplatform.one/keycloak
4
+ * File Created: 26-11-2022 06:58:16
5
+ * Author: Clay Risser
6
+ * -----
7
+ * Last Modified: 26-11-2022 08:38:25
8
+ * Modified By: Clay Risser
9
+ * -----
10
+ * Risser Labs LLC (c) Copyright 2022
11
+ *
12
+ * Licensed under the Apache License, Version 2.0 (the "License");
13
+ * you may not use this file except in compliance with the License.
14
+ * You may obtain a copy of the License at
15
+ *
16
+ * http://www.apache.org/licenses/LICENSE-2.0
17
+ *
18
+ * Unless required by applicable law or agreed to in writing, software
19
+ * distributed under the License is distributed on an "AS IS" BASIS,
20
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
+ * See the License for the specific language governing permissions and
22
+ * limitations under the License.
23
+ */
24
+
25
+ import {
26
+ AuthRequestPromptOptions,
27
+ AuthSessionResult,
28
+ TokenResponse,
29
+ } from "expo-auth-session";
30
+ import { createContext } from "react";
31
+ import { KC_INITIAL_VALUE } from "./const";
32
+
33
+ export const ExpoKeycloakContext = createContext<{
34
+ ready: boolean;
35
+ isLoggedIn?: boolean;
36
+ login: (
37
+ options?: AuthRequestPromptOptions
38
+ ) => Promise<AuthSessionResult | undefined>;
39
+ logout: () => undefined;
40
+ token: TokenResponse | null;
41
+ }>(KC_INITIAL_VALUE);
@@ -0,0 +1,139 @@
1
+ /**
2
+ * File: /src/expo/KeycloakProvider.ts
3
+ * Project: @multiplatform.one/keycloak
4
+ * File Created: 26-11-2022 06:58:16
5
+ * Author: Clay Risser
6
+ * -----
7
+ * Last Modified: 26-11-2022 08:06:40
8
+ * Modified By: Clay Risser
9
+ * -----
10
+ * Risser Labs LLC (c) Copyright 2022
11
+ *
12
+ * Licensed under the Apache License, Version 2.0 (the "License");
13
+ * you may not use this file except in compliance with the License.
14
+ * You may obtain a copy of the License at
15
+ *
16
+ * http://www.apache.org/licenses/LICENSE-2.0
17
+ *
18
+ * Unless required by applicable law or agreed to in writing, software
19
+ * distributed under the License is distributed on an "AS IS" BASIS,
20
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
+ * See the License for the specific language governing permissions and
22
+ * limitations under the License.
23
+ */
24
+
25
+ import React, { ReactNode, useCallback, useEffect } from "react";
26
+ import { Platform } from "react-native";
27
+ import * as AuthSession from "expo-auth-session";
28
+ import {
29
+ useAuthRequest,
30
+ useAutoDiscovery,
31
+ AuthRequestConfig,
32
+ } from "expo-auth-session";
33
+ import { ExpoKeycloakContext } from "./ExpoKeycloakContext";
34
+ import { useTokenStorage } from "./useTokenStorage";
35
+ import { handleTokenExchange, getRealmURL } from "./helpers";
36
+ import { NATIVE_REDIRECT_PATH } from "./const";
37
+
38
+ export interface IKeycloakConfiguration extends Partial<AuthRequestConfig> {
39
+ children: ReactNode;
40
+ clientId: string;
41
+ disableAutoRefresh?: boolean;
42
+ nativeRedirectPath?: string;
43
+ realm: string;
44
+ refreshTimeBuffer?: number;
45
+ scheme?: string;
46
+ tokenStorageKey?: string;
47
+ url: string;
48
+ }
49
+
50
+ export const ExpoKeycloakProvider = ({
51
+ realm,
52
+ clientId,
53
+ url,
54
+ extraParams,
55
+ children,
56
+ ...options
57
+ }: IKeycloakConfiguration) => {
58
+ const discovery = useAutoDiscovery(getRealmURL({ realm, url }));
59
+ const redirectUri = AuthSession.makeRedirectUri({
60
+ native: `${options.scheme ?? "exp"}://${
61
+ options.nativeRedirectPath ?? NATIVE_REDIRECT_PATH
62
+ }`,
63
+ useProxy: !options.scheme,
64
+ });
65
+ const config = {
66
+ clientId,
67
+ extraParams,
68
+ realm,
69
+ redirectUri,
70
+ url,
71
+ };
72
+ const [request, response, promptAsync] = useAuthRequest(
73
+ { usePKCE: false, ...config },
74
+ discovery
75
+ );
76
+ const [currentToken, updateToken] = useTokenStorage(
77
+ options,
78
+ config,
79
+ discovery!
80
+ );
81
+
82
+ const handleLogin = useCallback(
83
+ (options: AuthSession.AuthRequestPromptOptions = {}) =>
84
+ promptAsync(options),
85
+ [request]
86
+ );
87
+
88
+ function handleLogout() {
89
+ if (!currentToken) throw new Error("Not logged in.");
90
+ try {
91
+ if (discovery?.revocationEndpoint) {
92
+ AuthSession.revokeAsync(
93
+ { token: currentToken?.accessToken, ...config },
94
+ discovery
95
+ );
96
+ }
97
+ if (discovery?.endSessionEndpoint) {
98
+ fetch(`${discovery.endSessionEndpoint}`, {
99
+ method: "POST",
100
+ headers: {
101
+ "Content-Type": "application/x-www-form-urlencoded",
102
+ },
103
+ body: `client_id=${clientId}&refresh_token=${currentToken.refreshToken}`,
104
+ });
105
+ }
106
+ if (Platform.OS === "ios") {
107
+ AuthSession.dismiss();
108
+ }
109
+ } catch (error) {
110
+ console.log(error);
111
+ }
112
+ updateToken(null);
113
+ return undefined;
114
+ }
115
+
116
+ useEffect(() => {
117
+ if (response) {
118
+ handleTokenExchange({ response, discovery: discovery!, config }).then(
119
+ updateToken
120
+ );
121
+ }
122
+ }, [response]);
123
+
124
+ return (
125
+ <ExpoKeycloakContext.Provider
126
+ // eslint-disable-next-line react/jsx-no-constructed-context-values
127
+ value={{
128
+ isLoggedIn: currentToken === undefined ? undefined : !!currentToken,
129
+ login: handleLogin,
130
+ logout: handleLogout,
131
+ ready:
132
+ discovery !== null && request !== null && currentToken !== undefined,
133
+ token: currentToken,
134
+ }}
135
+ >
136
+ {children}
137
+ </ExpoKeycloakContext.Provider>
138
+ );
139
+ };
package/src/expo/NOTES ADDED
@@ -0,0 +1 @@
1
+ This folder contains code by rubhiauliatirta (https://github.com/rubhiauliatirta)
@@ -0,0 +1,42 @@
1
+ /**
2
+ * File: /src/expo/const.ts
3
+ * Project: @multiplatform.one/keycloak
4
+ * File Created: 26-11-2022 06:58:16
5
+ * Author: Clay Risser
6
+ * -----
7
+ * Last Modified: 26-11-2022 08:17:10
8
+ * Modified By: Clay Risser
9
+ * -----
10
+ * Risser Labs LLC (c) Copyright 2022
11
+ *
12
+ * Licensed under the Apache License, Version 2.0 (the "License");
13
+ * you may not use this file except in compliance with the License.
14
+ * You may obtain a copy of the License at
15
+ *
16
+ * http://www.apache.org/licenses/LICENSE-2.0
17
+ *
18
+ * Unless required by applicable law or agreed to in writing, software
19
+ * distributed under the License is distributed on an "AS IS" BASIS,
20
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
+ * See the License for the specific language governing permissions and
22
+ * limitations under the License.
23
+ */
24
+
25
+ const logger = console;
26
+
27
+ export const KC_INITIAL_VALUE = {
28
+ ready: false,
29
+ isLoggedIn: false,
30
+ login: async () => {
31
+ logger.error("kc not initialized");
32
+ return undefined;
33
+ },
34
+ logout: () => {
35
+ logger.error("not logged in");
36
+ return undefined;
37
+ },
38
+ token: null,
39
+ };
40
+ export const NATIVE_REDIRECT_PATH = "auth/redirect";
41
+ export const TOKEN_STORAGE_KEY = "keycloak_token";
42
+ export const REFRESH_TIME_BUFFER = 20;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * File: /src/expo/helpers.ts
3
+ * Project: @multiplatform.one/keycloak
4
+ * File Created: 26-11-2022 06:58:16
5
+ * Author: Clay Risser
6
+ * -----
7
+ * Last Modified: 26-11-2022 08:11:15
8
+ * Modified By: Clay Risser
9
+ * -----
10
+ * Risser Labs LLC (c) Copyright 2022
11
+ *
12
+ * Licensed under the Apache License, Version 2.0 (the "License");
13
+ * you may not use this file except in compliance with the License.
14
+ * You may obtain a copy of the License at
15
+ *
16
+ * http://www.apache.org/licenses/LICENSE-2.0
17
+ *
18
+ * Unless required by applicable law or agreed to in writing, software
19
+ * distributed under the License is distributed on an "AS IS" BASIS,
20
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
+ * See the License for the specific language governing permissions and
22
+ * limitations under the License.
23
+ */
24
+
25
+ import {
26
+ AccessTokenRequestConfig,
27
+ exchangeCodeAsync,
28
+ DiscoveryDocument,
29
+ AuthSessionResult,
30
+ } from "expo-auth-session";
31
+
32
+ export function getRealmURL(config: RealmURLConfig) {
33
+ const { url, realm } = config;
34
+ const slash = url.endsWith("/") ? "" : "/";
35
+ return `${url + slash}realms/${encodeURIComponent(realm)}`;
36
+ }
37
+
38
+ export function getCurrentTimeInSeconds() {
39
+ return Math.floor(Date.now() / 1000);
40
+ }
41
+
42
+ export async function handleTokenExchange({
43
+ response,
44
+ discovery,
45
+ config,
46
+ }: TokenExchange) {
47
+ try {
48
+ if (response?.type === "success" && !!discovery?.tokenEndpoint) {
49
+ return exchangeCodeAsync(
50
+ { code: response.params.code, ...config },
51
+ discovery
52
+ );
53
+ }
54
+ if (response?.type === "error") return null;
55
+ return null;
56
+ } catch (error) {
57
+ return null;
58
+ }
59
+ }
60
+
61
+ export interface TokenExchange {
62
+ response: AuthSessionResult;
63
+ discovery: Pick<DiscoveryDocument, "tokenEndpoint">;
64
+ config: Omit<AccessTokenRequestConfig, "code"> & { code?: string };
65
+ }
66
+
67
+ export interface TokenExchangeDiscovery {
68
+ tokenEndpoint?: string;
69
+ }
70
+
71
+ export interface RealmURLConfig {
72
+ realm: string;
73
+ url: string;
74
+ }
@@ -1,10 +1,10 @@
1
1
  /**
2
- * File: /src/@types/expoKeycloakAuth.d.ts
2
+ * File: /src/expo/index.ts
3
3
  * Project: @multiplatform.one/keycloak
4
- * File Created: 25-11-2022 10:22:18
4
+ * File Created: 26-11-2022 06:58:16
5
5
  * Author: Clay Risser
6
6
  * -----
7
- * Last Modified: 25-11-2022 10:22:24
7
+ * Last Modified: 26-11-2022 08:38:41
8
8
  * Modified By: Clay Risser
9
9
  * -----
10
10
  * Risser Labs LLC (c) Copyright 2022
@@ -22,7 +22,6 @@
22
22
  * limitations under the License.
23
23
  */
24
24
 
25
- declare module "expo-keycloak-auth" {
26
- export const KeycloakProvider: any;
27
- export const useKeycloak: any;
28
- }
25
+ export * from "./ExpoKeycloakProvider";
26
+ export * from "./useExpoKeycloak";
27
+ export * from "./const";
@@ -0,0 +1,44 @@
1
+ /**
2
+ * File: /src/expo/useExpoKeycloak.ts
3
+ * Project: @multiplatform.one/keycloak
4
+ * File Created: 26-11-2022 06:58:16
5
+ * Author: Clay Risser
6
+ * -----
7
+ * Last Modified: 26-11-2022 08:38:57
8
+ * Modified By: Clay Risser
9
+ * -----
10
+ * Risser Labs LLC (c) Copyright 2022
11
+ *
12
+ * Licensed under the Apache License, Version 2.0 (the "License");
13
+ * you may not use this file except in compliance with the License.
14
+ * You may obtain a copy of the License at
15
+ *
16
+ * http://www.apache.org/licenses/LICENSE-2.0
17
+ *
18
+ * Unless required by applicable law or agreed to in writing, software
19
+ * distributed under the License is distributed on an "AS IS" BASIS,
20
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
+ * See the License for the specific language governing permissions and
22
+ * limitations under the License.
23
+ */
24
+
25
+ import { useContext } from "react";
26
+ import { ExpoKeycloakContext } from "./ExpoKeycloakContext";
27
+
28
+ export function useExpoKeycloak() {
29
+ const {
30
+ isLoggedIn,
31
+ login,
32
+ logout,
33
+ ready = false,
34
+ token = null,
35
+ } = useContext(ExpoKeycloakContext);
36
+ return {
37
+ isLoggedIn,
38
+ login,
39
+ logout,
40
+ ready,
41
+ token: token?.accessToken ?? null,
42
+ refreshToken: token?.refreshToken ?? null,
43
+ };
44
+ }
@@ -0,0 +1,168 @@
1
+ /**
2
+ * File: /src/expo/useTokenStorage.ts
3
+ * Project: @multiplatform.one/keycloak
4
+ * File Created: 26-11-2022 06:58:16
5
+ * Author: Clay Risser
6
+ * -----
7
+ * Last Modified: 26-11-2022 08:08:00
8
+ * Modified By: Clay Risser
9
+ * -----
10
+ * Risser Labs LLC (c) Copyright 2022
11
+ *
12
+ * Licensed under the Apache License, Version 2.0 (the "License");
13
+ * you may not use this file except in compliance with the License.
14
+ * You may obtain a copy of the License at
15
+ *
16
+ * http://www.apache.org/licenses/LICENSE-2.0
17
+ *
18
+ * Unless required by applicable law or agreed to in writing, software
19
+ * distributed under the License is distributed on an "AS IS" BASIS,
20
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
+ * See the License for the specific language governing permissions and
22
+ * limitations under the License.
23
+ */
24
+
25
+ import { AppState, AppStateStatus, Platform } from "react-native";
26
+ import { DiscoveryDocument, refreshAsync } from "expo-auth-session";
27
+ import { REFRESH_TIME_BUFFER, TOKEN_STORAGE_KEY } from "./const";
28
+ import { dismiss, revokeAsync, TokenResponse } from "expo-auth-session";
29
+ import { getCurrentTimeInSeconds } from "./helpers";
30
+ import { useAsyncStorage } from "@react-native-async-storage/async-storage";
31
+ import { useEffect, useRef, useState } from "react";
32
+
33
+ const logger = console;
34
+
35
+ export function useTokenStorage(
36
+ {
37
+ disableAutoRefresh = false,
38
+ refreshTimeBuffer = REFRESH_TIME_BUFFER,
39
+ tokenStorageKey = TOKEN_STORAGE_KEY,
40
+ },
41
+ config: {
42
+ clientId: string;
43
+ extraParams?: Record<string, string>;
44
+ realm: string;
45
+ redirectUri: string;
46
+ url: string;
47
+ },
48
+ discovery: DiscoveryDocument
49
+ ): [TokenResponse | null, (token: TokenResponse | null) => Promise<void>] {
50
+ const [token, setToken] = useState<TokenResponse | null>();
51
+ const { getItem, setItem, removeItem } = useAsyncStorage(tokenStorageKey);
52
+ const refreshHandler = useRef<NodeJS.Timeout>(null);
53
+ const appState = useRef(AppState.currentState);
54
+ const refreshTime = useRef<number>(null);
55
+ const tokenData = useRef<TokenResponse>(null);
56
+
57
+ async function updateAndSaveToken(newToken: TokenResponse | null) {
58
+ try {
59
+ setToken(newToken);
60
+ if (newToken !== null) {
61
+ const stringifiedValue = JSON.stringify(newToken);
62
+ await setItem(stringifiedValue);
63
+ } else {
64
+ await removeItem();
65
+ }
66
+ } catch (error) {
67
+ logger.error(error);
68
+ }
69
+ }
70
+
71
+ async function handleTokenRefresh(token: TokenResponse | null) {
72
+ try {
73
+ const tokenResponse = await refreshAsync(
74
+ { refreshToken: token?.refreshToken, ...config },
75
+ discovery
76
+ );
77
+ updateAndSaveToken(tokenResponse);
78
+ } catch (err) {
79
+ updateAndSaveToken(null);
80
+ }
81
+ }
82
+
83
+ useEffect(() => {
84
+ function handleAppState(nextAppState: AppStateStatus) {
85
+ if (
86
+ appState.current.match(/inactive|background/) &&
87
+ nextAppState === "active"
88
+ ) {
89
+ if (refreshHandler.current !== null) {
90
+ clearTimeout(refreshHandler.current);
91
+ const now = getCurrentTimeInSeconds();
92
+ if (Number(refreshTime.current) <= now) {
93
+ setToken(null);
94
+ } else {
95
+ const timeout = 1000 * (Number(refreshTime.current) - now);
96
+ // @ts-ignore
97
+ refreshHandler.current = setTimeout(() => {
98
+ handleTokenRefresh(tokenData.current);
99
+ }, timeout);
100
+ }
101
+ }
102
+ }
103
+ appState.current = nextAppState;
104
+ }
105
+ const subscription = AppState.addEventListener("change", handleAppState);
106
+ return () => {
107
+ if (subscription) {
108
+ subscription.remove();
109
+ } else {
110
+ // @ts-ignore
111
+ AppState.removeEventListener("change", handleAppState);
112
+ }
113
+ };
114
+ }, []);
115
+
116
+ useEffect(() => {
117
+ async function getTokenFromStorage() {
118
+ try {
119
+ const tokenFromStorage = await getItem();
120
+ if (!tokenFromStorage) throw new Error("No token in storage");
121
+ const token = JSON.parse(tokenFromStorage);
122
+ if (!TokenResponse.isTokenFresh(token, -refreshTimeBuffer)) {
123
+ handleTokenRefresh(token);
124
+ } else {
125
+ setToken(token);
126
+ }
127
+ } catch (error) {
128
+ setToken(null);
129
+ }
130
+ }
131
+ if (discovery) getTokenFromStorage();
132
+ }, [discovery]);
133
+
134
+ useEffect(() => {
135
+ // trigger every token update
136
+ // @ts-ignore
137
+ tokenData.current = token || null;
138
+ if (token !== undefined && !disableAutoRefresh) {
139
+ if (refreshHandler.current !== null) {
140
+ clearTimeout(refreshHandler.current);
141
+ }
142
+ if (token?.expiresIn) {
143
+ const now = getCurrentTimeInSeconds();
144
+ // @ts-ignore
145
+ refreshTime.current =
146
+ token.issuedAt + token.expiresIn - refreshTimeBuffer;
147
+ const timeout = 1000 * (refreshTime.current - now);
148
+ // @ts-ignore
149
+ refreshHandler.current = setTimeout(() => {
150
+ handleTokenRefresh(token);
151
+ }, timeout);
152
+ }
153
+ if (token === null && tokenData.current !== null) {
154
+ revokeAsync(
155
+ { token: tokenData.current?.accessToken, ...config },
156
+ discovery
157
+ );
158
+ Platform.OS === "ios" && dismiss();
159
+ // @ts-ignore
160
+ refreshTime.current = null;
161
+ // @ts-ignore
162
+ tokenData.current = null;
163
+ }
164
+ }
165
+ }, [token]);
166
+
167
+ return [token || null, updateAndSaveToken];
168
+ }
@@ -4,7 +4,7 @@
4
4
  * File Created: 08-11-2022 14:10:44
5
5
  * Author: Clay Risser
6
6
  * -----
7
- * Last Modified: 25-11-2022 09:52:31
7
+ * Last Modified: 26-11-2022 08:31:01
8
8
  * Modified By: Clay Risser
9
9
  * -----
10
10
  * Risser Labs LLC (c) Copyright 2021 - 2022
@@ -22,13 +22,20 @@
22
22
  * limitations under the License.
23
23
  */
24
24
 
25
+ import type {
26
+ KeycloakResourceAccess,
27
+ KeycloakRoles,
28
+ KeycloakTokenParsed,
29
+ } from "keycloak-js";
25
30
  export * from "./useKeycloak";
26
31
 
27
32
  export interface IKeycloak {
28
33
  authenticated?: boolean;
29
34
  token?: string;
30
35
  refreshToken?: string;
31
- tokenParsed?: any;
32
- login: () => unknown;
33
- logout: () => unknown;
36
+ tokenParsed?: KeycloakTokenParsed;
37
+ realmAccess?: KeycloakRoles;
38
+ resourceAccess?: KeycloakResourceAccess;
39
+ login: () => Promise<unknown> | unknown;
40
+ logout: () => Promise<unknown> | unknown;
34
41
  }