capacitor-oidc 0.0.1-alpha.0 → 0.0.1-alpha.2
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/CapacitorOidc.podspec +3 -2
- package/Package.swift +1 -1
- package/README.md +31 -3
- package/SECURITY.md +31 -0
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/jojopirker/capacitor/oidc/CapacitorOidcPlugin.java +33 -27
- package/contracts/fixtures/stored-session-v1.json +11 -0
- package/contracts/stored-session-v1.schema.json +42 -0
- package/dist/esm/capacitor-navigator.js +14 -12
- package/dist/esm/capacitor-navigator.js.map +1 -1
- package/dist/esm/capacitor-secure-state-store.js +4 -20
- package/dist/esm/capacitor-secure-state-store.js.map +1 -1
- package/dist/esm/capacitor-user-manager.js +8 -13
- package/dist/esm/capacitor-user-manager.js.map +1 -1
- package/dist/esm/definitions.d.ts +3 -6
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/errors.js +1 -2
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/native.js +1 -2
- package/dist/esm/native.js.map +1 -1
- package/dist/plugin.cjs +28 -64
- package/dist/plugin.cjs.map +1 -1
- package/docs/PUBLISHING.md +86 -0
- package/docs/TESTING.md +47 -0
- package/docs/adr/0001-use-oidc-client-ts.md +22 -0
- package/docs/adr/0002-keep-the-native-boundary-small.md +16 -0
- package/docs/adr/0003-share-session-storage-with-widgets.md +20 -0
- package/ios/Sources/CapacitorOidcPlugin/CapacitorOidcPlugin.swift +52 -43
- package/package.json +13 -12
- package/android/src/main/java/com/jojopirker/capacitor/oidc/NativeContract.java +0 -35
- package/dist/esm/generated/native-contract.d.ts +0 -37
- package/dist/esm/generated/native-contract.js +0 -38
- package/dist/esm/generated/native-contract.js.map +0 -1
- package/ios/Sources/CapacitorOidcPlugin/NativeContract.generated.swift +0 -39
package/CapacitorOidc.podspec
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require 'json'
|
|
2
2
|
|
|
3
3
|
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
|
4
|
+
repository_url = package['repository']['url'].sub(/\Agit\+/, '')
|
|
4
5
|
|
|
5
6
|
Pod::Spec.new do |spec|
|
|
6
7
|
spec.name = 'CapacitorOidc'
|
|
@@ -9,9 +10,9 @@ Pod::Spec.new do |spec|
|
|
|
9
10
|
spec.license = package['license']
|
|
10
11
|
spec.homepage = package['homepage']
|
|
11
12
|
spec.author = package['author']
|
|
12
|
-
spec.source = { git:
|
|
13
|
+
spec.source = { git: repository_url, tag: spec.version.to_s }
|
|
13
14
|
spec.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
|
|
14
15
|
spec.ios.deployment_target = '15.0'
|
|
15
|
-
spec.dependency 'Capacitor'
|
|
16
|
+
spec.dependency 'Capacitor', '>= 7.0'
|
|
16
17
|
spec.swift_version = '5.9'
|
|
17
18
|
end
|
package/Package.swift
CHANGED
|
@@ -8,7 +8,7 @@ let package = Package(
|
|
|
8
8
|
.library(name: "CapacitorOidc", targets: ["CapacitorOidcPlugin"])
|
|
9
9
|
],
|
|
10
10
|
dependencies: [
|
|
11
|
-
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git",
|
|
11
|
+
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", "7.0.0"..<"9.0.0")
|
|
12
12
|
],
|
|
13
13
|
targets: [
|
|
14
14
|
.target(
|
package/README.md
CHANGED
|
@@ -20,6 +20,8 @@ npm install capacitor-oidc oidc-client-ts @capacitor/app
|
|
|
20
20
|
npx cap sync
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
The package supports Capacitor 7 and newer.
|
|
24
|
+
|
|
23
25
|
All discovery, token, refresh, UserInfo, and revocation requests use normal WebView `fetch`. The provider must allow the app's configured Capacitor origin through CORS.
|
|
24
26
|
|
|
25
27
|
Authorization and logout endpoints must use HTTPS. Plain HTTP is accepted only for loopback local development.
|
|
@@ -40,11 +42,11 @@ const manager = await CapacitorUserManager.create(
|
|
|
40
42
|
revokeTokensOnSignout: true,
|
|
41
43
|
},
|
|
42
44
|
{
|
|
45
|
+
prefersEphemeralWebBrowserSession: false,
|
|
43
46
|
storageNamespace: 'primary',
|
|
44
47
|
ios: {
|
|
45
48
|
keychainAccessGroup: 'TEAMID.group.com.example.app',
|
|
46
49
|
keychainAccessibility: 'afterFirstUnlockThisDeviceOnly',
|
|
47
|
-
prefersEphemeralWebBrowserSession: false,
|
|
48
50
|
},
|
|
49
51
|
},
|
|
50
52
|
);
|
|
@@ -65,9 +67,35 @@ Register the redirect URI as a native public-client redirect at the provider. Ne
|
|
|
65
67
|
|
|
66
68
|
For an iOS custom scheme, add it to the application target's `CFBundleURLTypes`. HTTPS callbacks through `ASWebAuthenticationSession` require iOS 17.4 or newer and the appropriate Associated Domains configuration.
|
|
67
69
|
|
|
68
|
-
For Android custom-scheme
|
|
70
|
+
For an Android custom-scheme redirect, keep the host app's `MainActivity` in Capacitor's default `singleTask` launch mode and add this intent filter inside that existing activity declaration. This ensures callbacks from Auth Tab's Custom Tab fallback reach the plugin instance that opened the session. Replace the scheme with the one used by your redirect URI:
|
|
71
|
+
|
|
72
|
+
```xml
|
|
73
|
+
<activity
|
|
74
|
+
android:name=".MainActivity"
|
|
75
|
+
android:launchMode="singleTask">
|
|
76
|
+
<intent-filter>
|
|
77
|
+
<action android:name="android.intent.action.VIEW" />
|
|
78
|
+
<category android:name="android.intent.category.DEFAULT" />
|
|
79
|
+
<category android:name="android.intent.category.BROWSABLE" />
|
|
80
|
+
<data android:scheme="com.example.app" />
|
|
81
|
+
</intent-filter>
|
|
82
|
+
</activity>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
HTTPS callbacks require a verified App Link and Digital Asset Links. Auth Tab handles the result directly when the installed browser supports it and falls back to a Custom Tab on older browsers.
|
|
86
|
+
|
|
87
|
+
Calling `cancel()` on Android rejects the pending JavaScript promise, but Android does not provide an API to forcibly close an already-open system Auth Tab or Custom Tab. Ephemeral browsing is requested on both platforms and may be ignored by an Android fallback browser.
|
|
88
|
+
|
|
89
|
+
Provider-specific logout parameters remain available through `oidc-client-ts`. For Amazon Cognito, leave `post_logout_redirect_uri` unset in the manager settings and pass its `client_id` and `logout_uri` parameters when signing out:
|
|
69
90
|
|
|
70
|
-
|
|
91
|
+
```ts
|
|
92
|
+
await manager.signout({
|
|
93
|
+
extraQueryParams: {
|
|
94
|
+
client_id: 'mobile-app',
|
|
95
|
+
logout_uri: 'com.example.app:/logout-callback',
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
```
|
|
71
99
|
|
|
72
100
|
## Secure storage and widgets
|
|
73
101
|
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
## Design boundary
|
|
4
|
+
|
|
5
|
+
`capacitor-oidc` delegates OAuth and OpenID Connect protocol behavior to
|
|
6
|
+
`oidc-client-ts`. Native code is limited to presenting system authentication UI
|
|
7
|
+
and storing state securely. The package does not patch `fetch`, process JWTs, or
|
|
8
|
+
implement OAuth endpoints in Swift or Java.
|
|
9
|
+
|
|
10
|
+
## Known limitation in the pinned OIDC engine
|
|
11
|
+
|
|
12
|
+
This prerelease pins `oidc-client-ts` 3.5.0. Its ID-token response validation
|
|
13
|
+
checks `sub`, `nonce`, and selected refresh-token continuity claims, but does not
|
|
14
|
+
check the required `iss`, `aud`, and `exp` claims. The behavior is visible in the
|
|
15
|
+
[v3.5.0 ResponseValidator](https://github.com/authts/oidc-client-ts/blob/v3.5.0/src/ResponseValidator.ts)
|
|
16
|
+
and is tracked in [authts/oidc-client-ts#2475](https://github.com/authts/oidc-client-ts/issues/2475).
|
|
17
|
+
|
|
18
|
+
The current alpha must not be treated as production-ready. Before a stable
|
|
19
|
+
release, use an upstream version that performs the required validation and cover
|
|
20
|
+
it with provider integration tests, or replace ADR 0001 with a different protocol
|
|
21
|
+
engine decision. Do not add package-local JWT or cryptographic validation as a
|
|
22
|
+
workaround.
|
|
23
|
+
|
|
24
|
+
## Runtime assumptions
|
|
25
|
+
|
|
26
|
+
`oidc-client-ts` requires `crypto.subtle` and `crypto.getRandomValues`.
|
|
27
|
+
`CapacitorUserManager.create()` rejects with `UNSUPPORTED_RUNTIME` when they are
|
|
28
|
+
absent. Their presence and behavior must also be verified in packaged applications
|
|
29
|
+
on physical iOS and Android devices.
|
|
30
|
+
|
|
31
|
+
See [Testing](docs/TESTING.md) for the remaining device and provider coverage.
|
package/android/build.gradle
CHANGED
|
@@ -16,9 +16,15 @@ import java.security.GeneralSecurityException;
|
|
|
16
16
|
import java.util.List;
|
|
17
17
|
import org.json.JSONObject;
|
|
18
18
|
|
|
19
|
-
@CapacitorPlugin(name =
|
|
19
|
+
@CapacitorPlugin(name = "CapacitorOidc")
|
|
20
20
|
public final class CapacitorOidcPlugin extends Plugin {
|
|
21
21
|
|
|
22
|
+
private static final String AUTH_SESSION_IN_PROGRESS = "AUTH_SESSION_IN_PROGRESS";
|
|
23
|
+
private static final String USER_CANCELLED = "USER_CANCELLED";
|
|
24
|
+
private static final String BROWSER_UNAVAILABLE = "BROWSER_UNAVAILABLE";
|
|
25
|
+
private static final String INVALID_CALLBACK = "INVALID_CALLBACK";
|
|
26
|
+
private static final String SECURE_STORAGE_ERROR = "SECURE_STORAGE_ERROR";
|
|
27
|
+
|
|
22
28
|
private ActivityResultLauncher<Intent> authLauncher;
|
|
23
29
|
private PluginCall pendingAuthCall;
|
|
24
30
|
private Uri pendingCallback;
|
|
@@ -38,26 +44,26 @@ public final class CapacitorOidcPlugin extends Plugin {
|
|
|
38
44
|
@PluginMethod
|
|
39
45
|
public void open(PluginCall call) {
|
|
40
46
|
if (pendingAuthCall != null) {
|
|
41
|
-
call.reject("An authentication session is already active.",
|
|
47
|
+
call.reject("An authentication session is already active.", AUTH_SESSION_IN_PROGRESS);
|
|
42
48
|
return;
|
|
43
49
|
}
|
|
44
50
|
|
|
45
|
-
String urlValue = requiredString(call,
|
|
46
|
-
String callbackValue = requiredString(call,
|
|
51
|
+
String urlValue = requiredString(call, "url");
|
|
52
|
+
String callbackValue = requiredString(call, "callbackUrl");
|
|
47
53
|
if (urlValue == null || callbackValue == null) return;
|
|
48
54
|
|
|
49
55
|
Uri url = Uri.parse(urlValue);
|
|
50
56
|
Uri callback = Uri.parse(callbackValue);
|
|
51
57
|
if (!isSecureRequestUrl(url)) {
|
|
52
|
-
call.reject("The authentication URL must use HTTPS outside loopback development.",
|
|
58
|
+
call.reject("The authentication URL must use HTTPS outside loopback development.", BROWSER_UNAVAILABLE);
|
|
53
59
|
return;
|
|
54
60
|
}
|
|
55
61
|
if (!isSupportedCallback(callback)) {
|
|
56
|
-
call.reject("The callback URL must use HTTPS or a custom scheme.",
|
|
62
|
+
call.reject("The callback URL must use HTTPS or a custom scheme.", INVALID_CALLBACK);
|
|
57
63
|
return;
|
|
58
64
|
}
|
|
59
65
|
|
|
60
|
-
boolean ephemeral = Boolean.TRUE.equals(call.getBoolean(
|
|
66
|
+
boolean ephemeral = Boolean.TRUE.equals(call.getBoolean("prefersEphemeralWebBrowserSession", false));
|
|
61
67
|
AuthTabIntent authTab = new AuthTabIntent.Builder().setEphemeralBrowsingEnabled(ephemeral).build();
|
|
62
68
|
pendingAuthCall = call;
|
|
63
69
|
pendingCallback = callback;
|
|
@@ -70,7 +76,7 @@ public final class CapacitorOidcPlugin extends Plugin {
|
|
|
70
76
|
}
|
|
71
77
|
} catch (ActivityNotFoundException | IllegalStateException error) {
|
|
72
78
|
clearPendingAuth();
|
|
73
|
-
call.reject("No compatible system browser is available.",
|
|
79
|
+
call.reject("No compatible system browser is available.", BROWSER_UNAVAILABLE);
|
|
74
80
|
}
|
|
75
81
|
}
|
|
76
82
|
|
|
@@ -78,7 +84,7 @@ public final class CapacitorOidcPlugin extends Plugin {
|
|
|
78
84
|
public void cancel(PluginCall call) {
|
|
79
85
|
PluginCall authCall = pendingAuthCall;
|
|
80
86
|
clearPendingAuth();
|
|
81
|
-
if (authCall != null) authCall.reject("The authentication session was cancelled.",
|
|
87
|
+
if (authCall != null) authCall.reject("The authentication session was cancelled.", USER_CANCELLED);
|
|
82
88
|
call.resolve();
|
|
83
89
|
}
|
|
84
90
|
|
|
@@ -90,15 +96,15 @@ public final class CapacitorOidcPlugin extends Plugin {
|
|
|
90
96
|
|
|
91
97
|
clearPendingAuth();
|
|
92
98
|
JSObject response = new JSObject();
|
|
93
|
-
response.put(
|
|
99
|
+
response.put("url", callback.toString());
|
|
94
100
|
call.resolve(response);
|
|
95
101
|
}
|
|
96
102
|
|
|
97
103
|
@PluginMethod
|
|
98
104
|
public void storageSet(PluginCall call) {
|
|
99
|
-
String namespace = requiredString(call,
|
|
100
|
-
String key = requiredString(call,
|
|
101
|
-
String value = call.getString(
|
|
105
|
+
String namespace = requiredString(call, "namespace");
|
|
106
|
+
String key = requiredString(call, "key");
|
|
107
|
+
String value = call.getString("value");
|
|
102
108
|
if (namespace == null || key == null) return;
|
|
103
109
|
if (value == null) {
|
|
104
110
|
call.reject("value is required.");
|
|
@@ -115,8 +121,8 @@ public final class CapacitorOidcPlugin extends Plugin {
|
|
|
115
121
|
|
|
116
122
|
@PluginMethod
|
|
117
123
|
public void storageGet(PluginCall call) {
|
|
118
|
-
String namespace = requiredString(call,
|
|
119
|
-
String key = requiredString(call,
|
|
124
|
+
String namespace = requiredString(call, "namespace");
|
|
125
|
+
String key = requiredString(call, "key");
|
|
120
126
|
if (namespace == null || key == null) return;
|
|
121
127
|
|
|
122
128
|
try {
|
|
@@ -128,8 +134,8 @@ public final class CapacitorOidcPlugin extends Plugin {
|
|
|
128
134
|
|
|
129
135
|
@PluginMethod
|
|
130
136
|
public void storageRemove(PluginCall call) {
|
|
131
|
-
String namespace = requiredString(call,
|
|
132
|
-
String key = requiredString(call,
|
|
137
|
+
String namespace = requiredString(call, "namespace");
|
|
138
|
+
String key = requiredString(call, "key");
|
|
133
139
|
if (namespace == null || key == null) return;
|
|
134
140
|
|
|
135
141
|
try {
|
|
@@ -141,13 +147,13 @@ public final class CapacitorOidcPlugin extends Plugin {
|
|
|
141
147
|
|
|
142
148
|
@PluginMethod
|
|
143
149
|
public void storageGetAllKeys(PluginCall call) {
|
|
144
|
-
String namespace = requiredString(call,
|
|
150
|
+
String namespace = requiredString(call, "namespace");
|
|
145
151
|
if (namespace == null) return;
|
|
146
152
|
|
|
147
153
|
try {
|
|
148
154
|
List<String> keys = vault.getAllKeys(namespace);
|
|
149
155
|
JSObject result = new JSObject();
|
|
150
|
-
result.put(
|
|
156
|
+
result.put("keys", new JSArray(keys));
|
|
151
157
|
call.resolve(result);
|
|
152
158
|
} catch (GeneralSecurityException | IOException error) {
|
|
153
159
|
rejectStorage(call);
|
|
@@ -156,11 +162,11 @@ public final class CapacitorOidcPlugin extends Plugin {
|
|
|
156
162
|
|
|
157
163
|
@PluginMethod
|
|
158
164
|
public void setSessionSnapshot(PluginCall call) {
|
|
159
|
-
String namespace = requiredString(call,
|
|
165
|
+
String namespace = requiredString(call, "namespace");
|
|
160
166
|
if (namespace == null) return;
|
|
161
167
|
|
|
162
168
|
try {
|
|
163
|
-
vault.setSessionSnapshot(namespace, call.getString(
|
|
169
|
+
vault.setSessionSnapshot(namespace, call.getString("value"));
|
|
164
170
|
call.resolve();
|
|
165
171
|
} catch (GeneralSecurityException | IOException error) {
|
|
166
172
|
rejectStorage(call);
|
|
@@ -174,20 +180,20 @@ public final class CapacitorOidcPlugin extends Plugin {
|
|
|
174
180
|
if (call == null) return;
|
|
175
181
|
|
|
176
182
|
if (result.resultCode == AuthTabIntent.RESULT_CANCELED) {
|
|
177
|
-
call.reject("The authentication session was cancelled.",
|
|
183
|
+
call.reject("The authentication session was cancelled.", USER_CANCELLED);
|
|
178
184
|
return;
|
|
179
185
|
}
|
|
180
186
|
if (result.resultCode != AuthTabIntent.RESULT_OK || result.resultUri == null) {
|
|
181
|
-
call.reject("The browser did not return a valid callback.",
|
|
187
|
+
call.reject("The browser did not return a valid callback.", INVALID_CALLBACK);
|
|
182
188
|
return;
|
|
183
189
|
}
|
|
184
190
|
if (!CallbackUriMatcher.matches(result.resultUri, expectedCallback)) {
|
|
185
|
-
call.reject("The authentication callback does not match the configured redirect URL.",
|
|
191
|
+
call.reject("The authentication callback does not match the configured redirect URL.", INVALID_CALLBACK);
|
|
186
192
|
return;
|
|
187
193
|
}
|
|
188
194
|
|
|
189
195
|
JSObject response = new JSObject();
|
|
190
|
-
response.put(
|
|
196
|
+
response.put("url", result.resultUri.toString());
|
|
191
197
|
call.resolve(response);
|
|
192
198
|
}
|
|
193
199
|
|
|
@@ -230,11 +236,11 @@ public final class CapacitorOidcPlugin extends Plugin {
|
|
|
230
236
|
|
|
231
237
|
private static void resolveValue(PluginCall call, String value) {
|
|
232
238
|
JSObject result = new JSObject();
|
|
233
|
-
result.put(
|
|
239
|
+
result.put("value", value == null ? JSONObject.NULL : value);
|
|
234
240
|
call.resolve(result);
|
|
235
241
|
}
|
|
236
242
|
|
|
237
243
|
private static void rejectStorage(PluginCall call) {
|
|
238
|
-
call.reject("Secure storage failed.",
|
|
244
|
+
call.reject("Secure storage failed.", SECURE_STORAGE_ERROR);
|
|
239
245
|
}
|
|
240
246
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"issuer": "https://issuer.example",
|
|
4
|
+
"clientId": "mobile",
|
|
5
|
+
"accessToken": "access",
|
|
6
|
+
"refreshToken": "refresh",
|
|
7
|
+
"idToken": "id-token",
|
|
8
|
+
"tokenType": "Bearer",
|
|
9
|
+
"scope": "openid profile offline_access",
|
|
10
|
+
"expiresAt": 1800000000
|
|
11
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/jojopirker/capacitor-oidc/contracts/stored-session-v1.schema.json",
|
|
4
|
+
"title": "StoredSessionV1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["version", "issuer", "clientId", "accessToken", "tokenType"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"version": {
|
|
10
|
+
"const": 1
|
|
11
|
+
},
|
|
12
|
+
"issuer": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"format": "uri"
|
|
15
|
+
},
|
|
16
|
+
"clientId": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"minLength": 1
|
|
19
|
+
},
|
|
20
|
+
"accessToken": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"minLength": 1
|
|
23
|
+
},
|
|
24
|
+
"refreshToken": {
|
|
25
|
+
"type": "string"
|
|
26
|
+
},
|
|
27
|
+
"idToken": {
|
|
28
|
+
"type": "string"
|
|
29
|
+
},
|
|
30
|
+
"tokenType": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"minLength": 1
|
|
33
|
+
},
|
|
34
|
+
"scope": {
|
|
35
|
+
"type": "string"
|
|
36
|
+
},
|
|
37
|
+
"expiresAt": {
|
|
38
|
+
"type": "integer",
|
|
39
|
+
"minimum": 0
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { CapacitorOidcError, unsupported } from './errors';
|
|
2
|
-
import { nativeContract } from './generated/native-contract';
|
|
3
2
|
import { NativeOidc } from './native';
|
|
4
3
|
export class CapacitorNavigator {
|
|
5
4
|
prefersEphemeralWebBrowserSession;
|
|
@@ -22,14 +21,14 @@ class CapacitorWindow {
|
|
|
22
21
|
assertSecureRequestUrl(url);
|
|
23
22
|
const callbackUrl = callbackUrlFromRequest(url);
|
|
24
23
|
const response = await NativeOidc.open({
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
url,
|
|
25
|
+
callbackUrl,
|
|
26
|
+
prefersEphemeralWebBrowserSession: this.prefersEphemeralWebBrowserSession,
|
|
28
27
|
});
|
|
29
|
-
if (!isExpectedCallback(response
|
|
30
|
-
throw new CapacitorOidcError(
|
|
28
|
+
if (!isExpectedCallback(response.url, callbackUrl)) {
|
|
29
|
+
throw new CapacitorOidcError('INVALID_CALLBACK', 'The authentication callback does not match the configured redirect URI');
|
|
31
30
|
}
|
|
32
|
-
return
|
|
31
|
+
return response;
|
|
33
32
|
}
|
|
34
33
|
close() {
|
|
35
34
|
void NativeOidc.cancel();
|
|
@@ -37,16 +36,19 @@ class CapacitorWindow {
|
|
|
37
36
|
}
|
|
38
37
|
export function assertSecureRequestUrl(requestUrl) {
|
|
39
38
|
const url = new URL(requestUrl);
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
const isHttpLoopback = url.protocol === 'http:' &&
|
|
40
|
+
(url.hostname === 'localhost' || url.hostname === '127.0.0.1' || url.hostname === '[::1]');
|
|
41
|
+
if (url.protocol !== 'https:' && !isHttpLoopback) {
|
|
42
|
+
throw new CapacitorOidcError('BROWSER_UNAVAILABLE', 'OIDC authorization and logout endpoints must use HTTPS');
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
export function callbackUrlFromRequest(requestUrl) {
|
|
46
46
|
const url = new URL(requestUrl);
|
|
47
|
-
const callback = url.searchParams.get('
|
|
47
|
+
const callback = url.searchParams.get('post_logout_redirect_uri') ??
|
|
48
|
+
url.searchParams.get('logout_uri') ??
|
|
49
|
+
url.searchParams.get('redirect_uri');
|
|
48
50
|
if (!callback) {
|
|
49
|
-
throw new CapacitorOidcError(
|
|
51
|
+
throw new CapacitorOidcError('INVALID_CALLBACK', 'The OIDC request does not contain a callback URI');
|
|
50
52
|
}
|
|
51
53
|
return callback;
|
|
52
54
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capacitor-navigator.js","sourceRoot":"","sources":["../../src/capacitor-navigator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC3D,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"capacitor-navigator.js","sourceRoot":"","sources":["../../src/capacitor-navigator.ts"],"names":[],"mappings":"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","sourcesContent":["import type { INavigator, IWindow, NavigateParams, NavigateResponse } from 'oidc-client-ts';\n\nimport { CapacitorOidcError, unsupported } from './errors';\nimport { NativeOidc } from './native';\n\nexport class CapacitorNavigator implements INavigator {\n constructor(private readonly prefersEphemeralWebBrowserSession: boolean) {}\n\n async prepare(): Promise<IWindow> {\n return new CapacitorWindow(this.prefersEphemeralWebBrowserSession);\n }\n\n async callback(): Promise<void> {\n unsupported('Navigator callbacks');\n }\n}\n\nclass CapacitorWindow implements IWindow {\n constructor(private readonly prefersEphemeralWebBrowserSession: boolean) {}\n\n async navigate({ url }: NavigateParams): Promise<NavigateResponse> {\n assertSecureRequestUrl(url);\n const callbackUrl = callbackUrlFromRequest(url);\n const response = await NativeOidc.open({\n url,\n callbackUrl,\n prefersEphemeralWebBrowserSession: this.prefersEphemeralWebBrowserSession,\n });\n\n if (!isExpectedCallback(response.url, callbackUrl)) {\n throw new CapacitorOidcError(\n 'INVALID_CALLBACK',\n 'The authentication callback does not match the configured redirect URI',\n );\n }\n\n return response;\n }\n\n close(): void {\n void NativeOidc.cancel();\n }\n}\n\nexport function assertSecureRequestUrl(requestUrl: string): void {\n const url = new URL(requestUrl);\n const isHttpLoopback =\n url.protocol === 'http:' &&\n (url.hostname === 'localhost' || url.hostname === '127.0.0.1' || url.hostname === '[::1]');\n if (url.protocol !== 'https:' && !isHttpLoopback) {\n throw new CapacitorOidcError('BROWSER_UNAVAILABLE', 'OIDC authorization and logout endpoints must use HTTPS');\n }\n}\n\nexport function callbackUrlFromRequest(requestUrl: string): string {\n const url = new URL(requestUrl);\n const callback =\n url.searchParams.get('post_logout_redirect_uri') ??\n url.searchParams.get('logout_uri') ??\n url.searchParams.get('redirect_uri');\n if (!callback) {\n throw new CapacitorOidcError('INVALID_CALLBACK', 'The OIDC request does not contain a callback URI');\n }\n return callback;\n}\n\nexport function isExpectedCallback(actualValue: string, expectedValue: string): boolean {\n const actual = new URL(actualValue);\n const expected = new URL(expectedValue);\n return (\n actual.protocol.toLowerCase() === expected.protocol.toLowerCase() &&\n actual.host.toLowerCase() === expected.host.toLowerCase() &&\n actual.pathname === expected.pathname\n );\n}\n\nexport class UnsupportedIframeNavigator implements INavigator {\n async prepare(): Promise<IWindow> {\n unsupported('Iframe navigation');\n }\n\n async callback(): Promise<void> {\n unsupported('Iframe navigation');\n }\n}\n"]}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { nativeContract } from './generated/native-contract';
|
|
2
1
|
import { NativeOidc } from './native';
|
|
3
2
|
export class CapacitorSecureStateStore {
|
|
4
3
|
namespace;
|
|
@@ -6,31 +5,16 @@ export class CapacitorSecureStateStore {
|
|
|
6
5
|
this.namespace = namespace;
|
|
7
6
|
}
|
|
8
7
|
async set(key, value) {
|
|
9
|
-
await NativeOidc.storageSet({
|
|
10
|
-
[nativeContract.fields.namespace]: this.namespace,
|
|
11
|
-
[nativeContract.fields.key]: key,
|
|
12
|
-
[nativeContract.fields.value]: value,
|
|
13
|
-
});
|
|
8
|
+
await NativeOidc.storageSet({ namespace: this.namespace, key, value });
|
|
14
9
|
}
|
|
15
10
|
async get(key) {
|
|
16
|
-
|
|
17
|
-
[nativeContract.fields.namespace]: this.namespace,
|
|
18
|
-
[nativeContract.fields.key]: key,
|
|
19
|
-
});
|
|
20
|
-
return result[nativeContract.fields.value];
|
|
11
|
+
return (await NativeOidc.storageGet({ namespace: this.namespace, key })).value;
|
|
21
12
|
}
|
|
22
13
|
async remove(key) {
|
|
23
|
-
|
|
24
|
-
[nativeContract.fields.namespace]: this.namespace,
|
|
25
|
-
[nativeContract.fields.key]: key,
|
|
26
|
-
});
|
|
27
|
-
return result[nativeContract.fields.value];
|
|
14
|
+
return (await NativeOidc.storageRemove({ namespace: this.namespace, key })).value;
|
|
28
15
|
}
|
|
29
16
|
async getAllKeys() {
|
|
30
|
-
|
|
31
|
-
[nativeContract.fields.namespace]: this.namespace,
|
|
32
|
-
});
|
|
33
|
-
return result[nativeContract.fields.keys];
|
|
17
|
+
return (await NativeOidc.storageGetAllKeys({ namespace: this.namespace })).keys;
|
|
34
18
|
}
|
|
35
19
|
}
|
|
36
20
|
//# sourceMappingURL=capacitor-secure-state-store.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capacitor-secure-state-store.js","sourceRoot":"","sources":["../../src/capacitor-secure-state-store.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"capacitor-secure-state-store.js","sourceRoot":"","sources":["../../src/capacitor-secure-state-store.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC,MAAM,OAAO,yBAAyB;IACP;IAA7B,YAA6B,SAAiB;QAAjB,cAAS,GAAT,SAAS,CAAQ;IAAG,CAAC;IAElD,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,KAAa;QAClC,MAAM,UAAU,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;IACzE,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,OAAO,CAAC,MAAM,UAAU,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;IACjF,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,OAAO,CAAC,MAAM,UAAU,CAAC,aAAa,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;IACpF,CAAC;IAED,KAAK,CAAC,UAAU;QACd,OAAO,CAAC,MAAM,UAAU,CAAC,iBAAiB,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAClF,CAAC;CACF","sourcesContent":["import type { StateStore } from 'oidc-client-ts';\n\nimport { NativeOidc } from './native';\n\nexport class CapacitorSecureStateStore implements StateStore {\n constructor(private readonly namespace: string) {}\n\n async set(key: string, value: string): Promise<void> {\n await NativeOidc.storageSet({ namespace: this.namespace, key, value });\n }\n\n async get(key: string): Promise<string | null> {\n return (await NativeOidc.storageGet({ namespace: this.namespace, key })).value;\n }\n\n async remove(key: string): Promise<string | null> {\n return (await NativeOidc.storageRemove({ namespace: this.namespace, key })).value;\n }\n\n async getAllKeys(): Promise<string[]> {\n return (await NativeOidc.storageGetAllKeys({ namespace: this.namespace })).keys;\n }\n}\n"]}
|
|
@@ -3,7 +3,6 @@ import { ErrorResponse, UserManager, } from 'oidc-client-ts';
|
|
|
3
3
|
import { CapacitorNavigator, UnsupportedIframeNavigator } from './capacitor-navigator';
|
|
4
4
|
import { CapacitorSecureStateStore } from './capacitor-secure-state-store';
|
|
5
5
|
import { CapacitorOidcError, unsupported } from './errors';
|
|
6
|
-
import { nativeContract } from './generated/native-contract';
|
|
7
6
|
import { NativeOidc } from './native';
|
|
8
7
|
export class CapacitorUserManager extends UserManager {
|
|
9
8
|
storageNamespace;
|
|
@@ -12,7 +11,7 @@ export class CapacitorUserManager extends UserManager {
|
|
|
12
11
|
constructor(settings, nativeOptions, storageNamespace) {
|
|
13
12
|
const stateStore = new CapacitorSecureStateStore(`${storageNamespace}.transactions`);
|
|
14
13
|
const userStore = new CapacitorSecureStateStore(`${storageNamespace}.session`);
|
|
15
|
-
const navigator = new CapacitorNavigator(nativeOptions.
|
|
14
|
+
const navigator = new CapacitorNavigator(nativeOptions.prefersEphemeralWebBrowserSession ?? false);
|
|
16
15
|
super({
|
|
17
16
|
...settings,
|
|
18
17
|
response_type: 'code',
|
|
@@ -28,11 +27,7 @@ export class CapacitorUserManager extends UserManager {
|
|
|
28
27
|
static async create(settings, nativeOptions = {}) {
|
|
29
28
|
assertRuntime();
|
|
30
29
|
assertSettings(settings);
|
|
31
|
-
await NativeOidc.configure({
|
|
32
|
-
[nativeContract.fields.prefersEphemeralWebBrowserSession]: nativeOptions.ios?.prefersEphemeralWebBrowserSession,
|
|
33
|
-
[nativeContract.fields.keychainAccessGroup]: nativeOptions.ios?.keychainAccessGroup,
|
|
34
|
-
[nativeContract.fields.keychainAccessibility]: nativeOptions.ios?.keychainAccessibility,
|
|
35
|
-
});
|
|
30
|
+
await NativeOidc.configure(nativeOptions.ios ?? {});
|
|
36
31
|
const manager = new CapacitorUserManager(settings, nativeOptions, nativeOptions.storageNamespace ?? 'default');
|
|
37
32
|
await manager.getValidUser();
|
|
38
33
|
manager.appStateListener = await App.addListener('appStateChange', ({ isActive }) => {
|
|
@@ -83,9 +78,9 @@ export class CapacitorUserManager extends UserManager {
|
|
|
83
78
|
async storeUser(user) {
|
|
84
79
|
await super.storeUser(user);
|
|
85
80
|
await NativeOidc.setSessionSnapshot({
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
? JSON.stringify(toStoredSession(user, this.settings.authority, this.settings.client_id))
|
|
81
|
+
namespace: this.storageNamespace,
|
|
82
|
+
value: user
|
|
83
|
+
? JSON.stringify(toStoredSession(user, user.profile.iss ?? this.settings.metadata?.issuer ?? this.settings.authority, this.settings.client_id))
|
|
89
84
|
: null,
|
|
90
85
|
});
|
|
91
86
|
}
|
|
@@ -133,15 +128,15 @@ export class CapacitorUserManager extends UserManager {
|
|
|
133
128
|
}
|
|
134
129
|
function assertRuntime() {
|
|
135
130
|
if (!globalThis.crypto?.subtle || !globalThis.crypto.getRandomValues) {
|
|
136
|
-
throw new CapacitorOidcError(
|
|
131
|
+
throw new CapacitorOidcError('UNSUPPORTED_RUNTIME', 'Web Crypto is required by oidc-client-ts');
|
|
137
132
|
}
|
|
138
133
|
}
|
|
139
134
|
function assertSettings(settings) {
|
|
140
135
|
if ('client_secret' in settings) {
|
|
141
|
-
throw new CapacitorOidcError(
|
|
136
|
+
throw new CapacitorOidcError('UNSUPPORTED_RUNTIME', 'Native public clients must not contain a client secret');
|
|
142
137
|
}
|
|
143
138
|
if ('client_authentication' in settings || 'dpop' in settings) {
|
|
144
|
-
throw new CapacitorOidcError(
|
|
139
|
+
throw new CapacitorOidcError('UNSUPPORTED_RUNTIME', 'The supplied settings contain an unsupported native-client option');
|
|
145
140
|
}
|
|
146
141
|
}
|
|
147
142
|
function toStoredSession(user, issuer, clientId) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capacitor-user-manager.js","sourceRoot":"","sources":["../../src/capacitor-user-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { App } from '@capacitor/app';\nimport type { PluginListenerHandle } from '@capacitor/core';\nimport {\n ErrorResponse,\n UserManager,\n type SigninPopupArgs,\n type SigninResourceOwnerCredentialsArgs,\n type SigninSilentArgs,\n type SignoutResponse,\n type SignoutPopupArgs,\n type User,\n} from 'oidc-client-ts';\n\nimport { CapacitorNavigator, UnsupportedIframeNavigator } from './capacitor-navigator';\nimport { CapacitorSecureStateStore } from './capacitor-secure-state-store';\nimport type { CapacitorOidcNativeOptions, CapacitorUserManagerSettings, StoredSessionV1 } from './definitions';\nimport { CapacitorOidcError, unsupported } from './errors';\nimport { nativeContract } from './generated/native-contract';\nimport { NativeOidc } from './native';\n\nexport class CapacitorUserManager extends UserManager {\n private refreshPromise?: Promise<User | null>;\n private appStateListener?: PluginListenerHandle;\n\n private constructor(\n settings: CapacitorUserManagerSettings,\n nativeOptions: CapacitorOidcNativeOptions,\n private readonly storageNamespace: string,\n ) {\n const stateStore = new CapacitorSecureStateStore(`${storageNamespace}.transactions`);\n const userStore = new CapacitorSecureStateStore(`${storageNamespace}.session`);\n const navigator = new CapacitorNavigator(nativeOptions.ios?.prefersEphemeralWebBrowserSession ?? false);\n\n super(\n {\n ...settings,\n response_type: 'code',\n disablePKCE: false,\n monitorSession: false,\n popup_redirect_uri: settings.redirect_uri,\n popup_post_logout_redirect_uri: settings.post_logout_redirect_uri,\n stateStore,\n userStore,\n },\n navigator,\n navigator,\n new UnsupportedIframeNavigator(),\n );\n }\n\n static async create(\n settings: CapacitorUserManagerSettings,\n nativeOptions: CapacitorOidcNativeOptions = {},\n ): Promise<CapacitorUserManager> {\n assertRuntime();\n assertSettings(settings);\n await NativeOidc.configure({\n [nativeContract.fields.prefersEphemeralWebBrowserSession]: nativeOptions.ios?.prefersEphemeralWebBrowserSession,\n [nativeContract.fields.keychainAccessGroup]: nativeOptions.ios?.keychainAccessGroup,\n [nativeContract.fields.keychainAccessibility]: nativeOptions.ios?.keychainAccessibility,\n });\n const manager = new CapacitorUserManager(settings, nativeOptions, nativeOptions.storageNamespace ?? 'default');\n await manager.getValidUser();\n manager.appStateListener = await App.addListener('appStateChange', ({ isActive }) => {\n if (isActive) {\n void manager\n .getValidUser()\n .catch((error: unknown) =>\n manager.events._raiseSilentRenewError(error instanceof Error ? error : new Error('Silent renewal failed')),\n );\n }\n });\n return manager;\n }\n\n async signin(args: SigninPopupArgs = {}): Promise<User> {\n return super.signinPopup(args);\n }\n\n async signout(args: SignoutPopupArgs = {}): Promise<void> {\n await super.signoutPopup(args);\n }\n\n async getValidUser(minimumValiditySeconds = 60): Promise<User | null> {\n const user = await this.getUser();\n if (!user) return null;\n if (user.expires_in === undefined || user.expires_in > minimumValiditySeconds) return user;\n return this.signinSilent();\n }\n\n override signinSilent(args: SigninSilentArgs = {}): Promise<User | null> {\n if (!this.refreshPromise) {\n this.refreshPromise = this.refresh(args).finally(() => {\n this.refreshPromise = undefined;\n });\n }\n return this.refreshPromise;\n }\n\n private async refresh(args: SigninSilentArgs): Promise<User | null> {\n const user = await this.getUser();\n if (!user?.refresh_token) {\n if (user?.expired) await this.removeUser();\n return null;\n }\n\n const refresh = super.signinSilent({ ...args, forceIframeAuth: false }).catch(async (error: unknown) => {\n if (error instanceof ErrorResponse && error.error === 'invalid_grant') await this.removeUser();\n throw error;\n });\n return refresh;\n }\n\n override async storeUser(user: User | null): Promise<void> {\n await super.storeUser(user);\n await NativeOidc.setSessionSnapshot({\n [nativeContract.fields.namespace]: this.storageNamespace,\n [nativeContract.fields.value]: user\n ? JSON.stringify(toStoredSession(user, this.settings.authority, this.settings.client_id))\n : null,\n });\n }\n\n async cancel(): Promise<void> {\n await NativeOidc.cancel();\n }\n\n async dispose(): Promise<void> {\n this.stopSilentRenew();\n await this.appStateListener?.remove();\n await this.cancel();\n }\n\n override async signinRedirect(): Promise<void> {\n unsupported('Redirect navigation');\n }\n\n override async signinRedirectCallback(_url?: string): Promise<User> {\n unsupported('Redirect navigation');\n }\n\n override async signinCallback(_url?: string): Promise<User | undefined> {\n unsupported('Browser callback dispatch');\n }\n\n override async signinResourceOwnerCredentials(_args: SigninResourceOwnerCredentialsArgs): Promise<User> {\n unsupported('Resource Owner Password Credentials');\n }\n\n override async signinSilentCallback(_url?: string): Promise<void> {\n unsupported('Iframe navigation');\n }\n\n override async signoutRedirect(): Promise<void> {\n unsupported('Redirect navigation');\n }\n\n override async signoutRedirectCallback(_url?: string): Promise<SignoutResponse> {\n unsupported('Redirect navigation');\n }\n\n override async signoutCallback(_url?: string, _keepOpen?: boolean): Promise<SignoutResponse | undefined> {\n unsupported('Browser callback dispatch');\n }\n\n override async signoutSilent(): Promise<void> {\n unsupported('Iframe logout');\n }\n\n override async signoutSilentCallback(_url?: string): Promise<void> {\n unsupported('Iframe logout');\n }\n\n override async querySessionStatus(): Promise<null> {\n unsupported('Browser session monitoring');\n }\n}\n\nfunction assertRuntime(): void {\n if (!globalThis.crypto?.subtle || !globalThis.crypto.getRandomValues) {\n throw new CapacitorOidcError(\n nativeContract.errorCodes.unsupportedRuntime,\n 'Web Crypto is required by oidc-client-ts',\n );\n }\n}\n\nfunction assertSettings(settings: CapacitorUserManagerSettings): void {\n if ('client_secret' in settings) {\n throw new CapacitorOidcError(\n nativeContract.errorCodes.unsupportedRuntime,\n 'Native public clients must not contain a client secret',\n );\n }\n if ('client_authentication' in settings || 'dpop' in settings) {\n throw new CapacitorOidcError(\n nativeContract.errorCodes.unsupportedRuntime,\n 'The supplied settings contain an unsupported native-client option',\n );\n }\n}\n\nfunction toStoredSession(user: User, issuer: string, clientId: string): StoredSessionV1 {\n return {\n version: 1,\n issuer,\n clientId,\n accessToken: user.access_token,\n refreshToken: user.refresh_token,\n idToken: user.id_token,\n tokenType: user.token_type,\n scope: user.scope,\n expiresAt: user.expires_at,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"capacitor-user-manager.js","sourceRoot":"","sources":["../../src/capacitor-user-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { App } from '@capacitor/app';\nimport type { PluginListenerHandle } from '@capacitor/core';\nimport {\n ErrorResponse,\n UserManager,\n type SigninPopupArgs,\n type SigninResourceOwnerCredentialsArgs,\n type SigninSilentArgs,\n type SignoutResponse,\n type SignoutPopupArgs,\n type User,\n} from 'oidc-client-ts';\n\nimport { CapacitorNavigator, UnsupportedIframeNavigator } from './capacitor-navigator';\nimport { CapacitorSecureStateStore } from './capacitor-secure-state-store';\nimport type { CapacitorOidcNativeOptions, CapacitorUserManagerSettings, StoredSessionV1 } from './definitions';\nimport { CapacitorOidcError, unsupported } from './errors';\nimport { NativeOidc } from './native';\n\nexport class CapacitorUserManager extends UserManager {\n private refreshPromise?: Promise<User | null>;\n private appStateListener?: PluginListenerHandle;\n\n private constructor(\n settings: CapacitorUserManagerSettings,\n nativeOptions: CapacitorOidcNativeOptions,\n private readonly storageNamespace: string,\n ) {\n const stateStore = new CapacitorSecureStateStore(`${storageNamespace}.transactions`);\n const userStore = new CapacitorSecureStateStore(`${storageNamespace}.session`);\n const navigator = new CapacitorNavigator(nativeOptions.prefersEphemeralWebBrowserSession ?? false);\n\n super(\n {\n ...settings,\n response_type: 'code',\n disablePKCE: false,\n monitorSession: false,\n popup_redirect_uri: settings.redirect_uri,\n popup_post_logout_redirect_uri: settings.post_logout_redirect_uri,\n stateStore,\n userStore,\n },\n navigator,\n navigator,\n new UnsupportedIframeNavigator(),\n );\n }\n\n static async create(\n settings: CapacitorUserManagerSettings,\n nativeOptions: CapacitorOidcNativeOptions = {},\n ): Promise<CapacitorUserManager> {\n assertRuntime();\n assertSettings(settings);\n await NativeOidc.configure(nativeOptions.ios ?? {});\n const manager = new CapacitorUserManager(settings, nativeOptions, nativeOptions.storageNamespace ?? 'default');\n await manager.getValidUser();\n manager.appStateListener = await App.addListener('appStateChange', ({ isActive }) => {\n if (isActive) {\n void manager\n .getValidUser()\n .catch((error: unknown) =>\n manager.events._raiseSilentRenewError(error instanceof Error ? error : new Error('Silent renewal failed')),\n );\n }\n });\n return manager;\n }\n\n async signin(args: SigninPopupArgs = {}): Promise<User> {\n return super.signinPopup(args);\n }\n\n async signout(args: SignoutPopupArgs = {}): Promise<void> {\n await super.signoutPopup(args);\n }\n\n async getValidUser(minimumValiditySeconds = 60): Promise<User | null> {\n const user = await this.getUser();\n if (!user) return null;\n if (user.expires_in === undefined || user.expires_in > minimumValiditySeconds) return user;\n return this.signinSilent();\n }\n\n override signinSilent(args: SigninSilentArgs = {}): Promise<User | null> {\n if (!this.refreshPromise) {\n this.refreshPromise = this.refresh(args).finally(() => {\n this.refreshPromise = undefined;\n });\n }\n return this.refreshPromise;\n }\n\n private async refresh(args: SigninSilentArgs): Promise<User | null> {\n const user = await this.getUser();\n if (!user?.refresh_token) {\n if (user?.expired) await this.removeUser();\n return null;\n }\n\n const refresh = super.signinSilent({ ...args, forceIframeAuth: false }).catch(async (error: unknown) => {\n if (error instanceof ErrorResponse && error.error === 'invalid_grant') await this.removeUser();\n throw error;\n });\n return refresh;\n }\n\n override async storeUser(user: User | null): Promise<void> {\n await super.storeUser(user);\n await NativeOidc.setSessionSnapshot({\n namespace: this.storageNamespace,\n value: user\n ? JSON.stringify(\n toStoredSession(\n user,\n user.profile.iss ?? this.settings.metadata?.issuer ?? this.settings.authority,\n this.settings.client_id,\n ),\n )\n : null,\n });\n }\n\n async cancel(): Promise<void> {\n await NativeOidc.cancel();\n }\n\n async dispose(): Promise<void> {\n this.stopSilentRenew();\n await this.appStateListener?.remove();\n await this.cancel();\n }\n\n override async signinRedirect(): Promise<void> {\n unsupported('Redirect navigation');\n }\n\n override async signinRedirectCallback(_url?: string): Promise<User> {\n unsupported('Redirect navigation');\n }\n\n override async signinCallback(_url?: string): Promise<User | undefined> {\n unsupported('Browser callback dispatch');\n }\n\n override async signinResourceOwnerCredentials(_args: SigninResourceOwnerCredentialsArgs): Promise<User> {\n unsupported('Resource Owner Password Credentials');\n }\n\n override async signinSilentCallback(_url?: string): Promise<void> {\n unsupported('Iframe navigation');\n }\n\n override async signoutRedirect(): Promise<void> {\n unsupported('Redirect navigation');\n }\n\n override async signoutRedirectCallback(_url?: string): Promise<SignoutResponse> {\n unsupported('Redirect navigation');\n }\n\n override async signoutCallback(_url?: string, _keepOpen?: boolean): Promise<SignoutResponse | undefined> {\n unsupported('Browser callback dispatch');\n }\n\n override async signoutSilent(): Promise<void> {\n unsupported('Iframe logout');\n }\n\n override async signoutSilentCallback(_url?: string): Promise<void> {\n unsupported('Iframe logout');\n }\n\n override async querySessionStatus(): Promise<null> {\n unsupported('Browser session monitoring');\n }\n}\n\nfunction assertRuntime(): void {\n if (!globalThis.crypto?.subtle || !globalThis.crypto.getRandomValues) {\n throw new CapacitorOidcError('UNSUPPORTED_RUNTIME', 'Web Crypto is required by oidc-client-ts');\n }\n}\n\nfunction assertSettings(settings: CapacitorUserManagerSettings): void {\n if ('client_secret' in settings) {\n throw new CapacitorOidcError('UNSUPPORTED_RUNTIME', 'Native public clients must not contain a client secret');\n }\n if ('client_authentication' in settings || 'dpop' in settings) {\n throw new CapacitorOidcError(\n 'UNSUPPORTED_RUNTIME',\n 'The supplied settings contain an unsupported native-client option',\n );\n }\n}\n\nfunction toStoredSession(user: User, issuer: string, clientId: string): StoredSessionV1 {\n return {\n version: 1,\n issuer,\n clientId,\n accessToken: user.access_token,\n refreshToken: user.refresh_token,\n idToken: user.id_token,\n tokenType: user.token_type,\n scope: user.scope,\n expiresAt: user.expires_at,\n };\n}\n"]}
|