capacitor-oidc 0.0.6 → 0.1.1
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/README.md +32 -25
- package/SECURITY.md +27 -30
- package/dist/esm/base-capacitor-user-manager.d.ts +30 -0
- package/dist/esm/base-capacitor-user-manager.js +104 -0
- package/dist/esm/base-capacitor-user-manager.js.map +1 -0
- package/dist/esm/capacitor-user-manager.d.ts +9 -32
- package/dist/esm/capacitor-user-manager.js +20 -174
- package/dist/esm/capacitor-user-manager.js.map +1 -1
- package/dist/esm/configuration.d.ts +14 -0
- package/dist/esm/configuration.js +72 -0
- package/dist/esm/configuration.js.map +1 -0
- package/dist/esm/definitions.d.ts +53 -3
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.d.ts +5 -4
- package/dist/esm/index.js +8 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/native-capacitor-user-manager.d.ts +24 -0
- package/dist/esm/native-capacitor-user-manager.js +112 -0
- package/dist/esm/native-capacitor-user-manager.js.map +1 -0
- package/dist/esm/web-capacitor-user-manager.d.ts +11 -0
- package/dist/esm/web-capacitor-user-manager.js +44 -0
- package/dist/esm/web-capacitor-user-manager.js.map +1 -0
- package/dist/plugin.cjs +303 -126
- package/dist/plugin.cjs.map +1 -1
- package/docs/API.md +106 -60
- package/docs/GETTING_STARTED.md +85 -62
- package/docs/PLATFORM_SETUP.md +15 -4
- package/docs/PROVIDERS.md +52 -45
- package/docs/README.md +4 -4
- package/docs/TESTING.md +3 -1
- package/docs/TROUBLESHOOTING.md +23 -13
- package/docs/adr/0004-use-one-manager-across-platforms.md +64 -0
- package/docs/adr/index.md +6 -0
- package/docs/frameworks/ANGULAR.md +129 -0
- package/docs/frameworks/REACT.md +134 -0
- package/docs/frameworks/VUE.md +118 -0
- package/package.json +8 -2
package/docs/TROUBLESHOOTING.md
CHANGED
|
@@ -15,6 +15,12 @@ The authorization request's `redirect_uri` must exactly match a callback
|
|
|
15
15
|
registered for the provider client. Check scheme, host, port, path, case, and
|
|
16
16
|
trailing slashes. Wildcard callback hosts are commonly unsupported.
|
|
17
17
|
|
|
18
|
+
## Web login returns without a user
|
|
19
|
+
|
|
20
|
+
Make the configured web callback route call `signinCallback()`. A logout callback
|
|
21
|
+
route must call `signoutCallback()` instead. Both routes must use exactly the URI
|
|
22
|
+
registered with the provider.
|
|
23
|
+
|
|
18
24
|
## Android does not return to the app
|
|
19
25
|
|
|
20
26
|
- Keep `MainActivity` in `singleTask` launch mode.
|
|
@@ -33,9 +39,10 @@ and that the callback scheme is registered in the target.
|
|
|
33
39
|
|
|
34
40
|
## Token, UserInfo, refresh, or revocation fails with a network error
|
|
35
41
|
|
|
36
|
-
These requests use the
|
|
37
|
-
to allow
|
|
38
|
-
not remove CORS requirements from token, UserInfo, refresh, or
|
|
42
|
+
These requests use the runtime's normal `fetch`. Configure the provider endpoint
|
|
43
|
+
to allow both web application and Capacitor origins through CORS. Static discovery
|
|
44
|
+
metadata does not remove CORS requirements from token, UserInfo, refresh, or
|
|
45
|
+
revocation calls.
|
|
39
46
|
|
|
40
47
|
## No refresh token is available
|
|
41
48
|
|
|
@@ -43,22 +50,25 @@ not remove CORS requirements from token, UserInfo, refresh, or revocation calls.
|
|
|
43
50
|
- Enable the refresh-token grant for the public client.
|
|
44
51
|
- Check provider-specific consent and rotation settings.
|
|
45
52
|
|
|
46
|
-
|
|
47
|
-
|
|
53
|
+
On native platforms, an expired user without a refresh token is removed and
|
|
54
|
+
`getValidUser()` returns `null`. Web behavior follows the configured upstream
|
|
55
|
+
silent-renew settings and may use an iframe.
|
|
48
56
|
|
|
49
57
|
## Startup renewal fails
|
|
50
58
|
|
|
51
|
-
`CapacitorUserManager.create()` restores secure local state
|
|
52
|
-
token renewal. When `automaticSilentRenew` is enabled, a
|
|
53
|
-
runs asynchronously and reports failures through
|
|
54
|
-
token-endpoint failures preserve the stored
|
|
55
|
-
removes it.
|
|
59
|
+
On native platforms, `CapacitorUserManager.create()` restores secure local state
|
|
60
|
+
but does not wait for token renewal. When `automaticSilentRenew` is enabled, a
|
|
61
|
+
required startup renewal runs asynchronously and reports failures through
|
|
62
|
+
`silentRenewError`. Temporary token-endpoint failures preserve the stored
|
|
63
|
+
session; a terminal `invalid_grant` removes it. Web automatic renewal follows
|
|
64
|
+
`oidc-client-ts` behavior.
|
|
56
65
|
|
|
57
66
|
## Logout does not return to the app
|
|
58
67
|
|
|
59
|
-
Register the post-logout URI at the provider
|
|
60
|
-
|
|
61
|
-
[Provider
|
|
68
|
+
Register the post-logout URI at the provider, add the browser callback route, and
|
|
69
|
+
register native schemes in each native application. Some providers use
|
|
70
|
+
non-standard parameters. Amazon Cognito uses `logout_uri`; see [Provider
|
|
71
|
+
configuration](PROVIDERS.md#amazon-cognito).
|
|
62
72
|
|
|
63
73
|
## `USER_CANCELLED` appears but Android UI remains visible
|
|
64
74
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# ADR 0004: Use one manager across platforms
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
The first package boundary was intentionally native-only. It kept the custom
|
|
10
|
+
code small by adapting `oidc-client-ts` only where Capacitor needed native system
|
|
11
|
+
authentication UI and secure storage. Browser methods were rejected, and the
|
|
12
|
+
documentation told applications to instantiate `oidc-client-ts` directly for
|
|
13
|
+
web.
|
|
14
|
+
|
|
15
|
+
That boundary moved platform selection into every consuming application. A
|
|
16
|
+
cross-platform app had to duplicate common settings, create two manager types,
|
|
17
|
+
branch for sign-in, callbacks, refresh, and logout, and depend directly on the
|
|
18
|
+
underlying library. The package abstracted native mechanics but did not provide
|
|
19
|
+
the originally intended application-level abstraction.
|
|
20
|
+
|
|
21
|
+
## Decision
|
|
22
|
+
|
|
23
|
+
`CapacitorUserManager` is the public manager for web, iOS, and Android.
|
|
24
|
+
Configuration is split into:
|
|
25
|
+
|
|
26
|
+
- required `common` public-client settings;
|
|
27
|
+
- optional `web` settings and redirect-or-popup interaction choices;
|
|
28
|
+
- optional `native` settings and native options;
|
|
29
|
+
- optional `ios` and `android` overrides of the native section.
|
|
30
|
+
|
|
31
|
+
Resolution is shallow and happens once:
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
web: common -> web
|
|
35
|
+
iOS: common -> native -> ios
|
|
36
|
+
Android: common -> native -> android
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Web uses the upstream browser navigators and configured browser stores. Native
|
|
40
|
+
uses `CapacitorNavigator`, secure stores, refresh-token-only renewal, and the app
|
|
41
|
+
resume listener. `signin()` and `signout()` return `Promise<void>` on all
|
|
42
|
+
platforms; applications read session state through events or user accessors.
|
|
43
|
+
|
|
44
|
+
The public manager is a common base and factory backed by internal web and
|
|
45
|
+
native implementations. Legacy native input is normalized before implementation
|
|
46
|
+
selection, so both factory signatures use the same native behavior.
|
|
47
|
+
|
|
48
|
+
All platforms remain public clients using code flow with PKCE. Web-only
|
|
49
|
+
capabilities stay in the web settings. Native does not accept custom stores,
|
|
50
|
+
DPoP, iframe callbacks, or browser session monitoring.
|
|
51
|
+
|
|
52
|
+
## Consequences
|
|
53
|
+
|
|
54
|
+
- Applications no longer branch between two manager implementations or import
|
|
55
|
+
`oidc-client-ts` directly for normal use.
|
|
56
|
+
- Web callbacks remain explicit application routes through `signinCallback()`
|
|
57
|
+
and `signoutCallback()`.
|
|
58
|
+
- Platform-specific provider values and interaction arguments can differ without
|
|
59
|
+
duplicating common configuration.
|
|
60
|
+
- The pre-1.0 native factory signature is normalized into the new implementation
|
|
61
|
+
as a deprecated migration path. The `Promise<void>` sign-in contract remains a
|
|
62
|
+
return-type change for applications that previously used the returned user.
|
|
63
|
+
- The package still owns no OIDC protocol implementation or provider-specific
|
|
64
|
+
behavior.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Architecture decisions
|
|
2
|
+
|
|
3
|
+
- [Use `oidc-client-ts`](0001-use-oidc-client-ts.md)
|
|
4
|
+
- [Keep the native boundary small](0002-keep-the-native-boundary-small.md)
|
|
5
|
+
- [Share session storage with widgets](0003-share-session-storage-with-widgets.md)
|
|
6
|
+
- [Use one manager across platforms](0004-use-one-manager-across-platforms.md)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Angular integration
|
|
2
|
+
|
|
3
|
+
The [Angular example](/examples/angular/) keeps one OIDC manager in a root
|
|
4
|
+
service. The service completes browser callbacks, restores the current user,
|
|
5
|
+
and exposes signals for templates.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install capacitor-oidc @capacitor/app
|
|
11
|
+
npx cap sync
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Configure the provider
|
|
15
|
+
|
|
16
|
+
Keep deploy-specific values in an Angular environment file:
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
// src/environments/environment.ts
|
|
20
|
+
export const environment = {
|
|
21
|
+
authority: 'https://identity.example.com',
|
|
22
|
+
clientId: 'public-app',
|
|
23
|
+
};
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Create a root service
|
|
27
|
+
|
|
28
|
+
`providedIn: 'root'` gives the application one manager, independent of route
|
|
29
|
+
and component lifetimes:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
@Injectable({ providedIn: 'root' })
|
|
33
|
+
export class OidcService {
|
|
34
|
+
readonly user = signal<User | null>(null);
|
|
35
|
+
|
|
36
|
+
private readonly manager = CapacitorUserManager.create({
|
|
37
|
+
common: {
|
|
38
|
+
authority: environment.authority,
|
|
39
|
+
client_id: environment.clientId,
|
|
40
|
+
scope: 'openid profile offline_access',
|
|
41
|
+
automaticSilentRenew: false,
|
|
42
|
+
},
|
|
43
|
+
web: {
|
|
44
|
+
settings: {
|
|
45
|
+
redirect_uri: `${window.location.origin}/callback`,
|
|
46
|
+
post_logout_redirect_uri: `${window.location.origin}/logout-callback`,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
native: {
|
|
50
|
+
settings: {
|
|
51
|
+
redirect_uri: 'capacitor-oidc-example:/callback',
|
|
52
|
+
post_logout_redirect_uri: 'capacitor-oidc-example:/logout-callback',
|
|
53
|
+
},
|
|
54
|
+
options: { storageNamespace: 'angular-example' },
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The runnable example keeps automatic renewal off so the bundled realm's
|
|
61
|
+
30-second test tokens can be renewed with the example's button. For a normal
|
|
62
|
+
provider, enable automatic renewal and keep its expiry-notification threshold
|
|
63
|
+
below the provider's access-token lifetime.
|
|
64
|
+
|
|
65
|
+
## Initialize from the root component
|
|
66
|
+
|
|
67
|
+
The service handles browser callbacks before exposing the restored user:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
async initialize() {
|
|
71
|
+
const auth = await this.manager;
|
|
72
|
+
|
|
73
|
+
auth.events.addUserLoaded((user) => this.user.set(user));
|
|
74
|
+
auth.events.addUserUnloaded(() => this.user.set(null));
|
|
75
|
+
|
|
76
|
+
if (window.location.pathname === '/callback') {
|
|
77
|
+
await auth.signinCallback();
|
|
78
|
+
window.history.replaceState({}, '', '/');
|
|
79
|
+
} else if (window.location.pathname === '/logout-callback') {
|
|
80
|
+
await auth.signoutCallback();
|
|
81
|
+
window.history.replaceState({}, '', '/');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
this.user.set(await auth.getUser());
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Call it once from the root component:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
export class AppComponent implements OnInit {
|
|
92
|
+
readonly oidc = inject(OidcService);
|
|
93
|
+
|
|
94
|
+
ngOnInit() {
|
|
95
|
+
void this.oidc.initialize();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Native callbacks finish inside the plugin and do not need Angular Router
|
|
101
|
+
routes.
|
|
102
|
+
|
|
103
|
+
## Bind actions
|
|
104
|
+
|
|
105
|
+
The root service can expose `signin()`, `getValidUser(60)`, and `signout()` as
|
|
106
|
+
methods. Components read the current `user` signal and do not hold their own
|
|
107
|
+
manager.
|
|
108
|
+
|
|
109
|
+
Call `dispose()` only when the Angular application is shutting down.
|
|
110
|
+
|
|
111
|
+
## Run the example
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
npm ci
|
|
115
|
+
npm run build
|
|
116
|
+
npm run e2e:keycloak:up
|
|
117
|
+
npm --prefix examples/angular install
|
|
118
|
+
npm --prefix examples/angular run dev
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Sign in with the disposable `demo` / `demo` account. Stop the local realm with
|
|
122
|
+
`npm run e2e:keycloak:down` when finished.
|
|
123
|
+
|
|
124
|
+
This local realm workflow is for the browser example. Android resolves
|
|
125
|
+
`localhost` inside the device or emulator, and native platforms should not use
|
|
126
|
+
the example's cleartext HTTP authority. Before building for iOS or Android,
|
|
127
|
+
replace `environment.authority` with an HTTPS authority reachable from that
|
|
128
|
+
device and register the configured native callback scheme at the provider. See
|
|
129
|
+
[iOS and Android setup](../PLATFORM_SETUP.md).
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# React integration
|
|
2
|
+
|
|
3
|
+
The [React example](/examples/react/) creates its OIDC manager outside the
|
|
4
|
+
component tree, restores the user when the application mounts, and keeps React
|
|
5
|
+
state aligned with the manager's user events.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install capacitor-oidc @capacitor/app
|
|
11
|
+
npx cap sync
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Create the manager outside React
|
|
15
|
+
|
|
16
|
+
A module-level promise prevents component renders from constructing multiple
|
|
17
|
+
managers:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
// src/auth.ts
|
|
21
|
+
import { CapacitorUserManager } from 'capacitor-oidc';
|
|
22
|
+
|
|
23
|
+
const manager = CapacitorUserManager.create({
|
|
24
|
+
common: {
|
|
25
|
+
authority: import.meta.env.VITE_OIDC_AUTHORITY,
|
|
26
|
+
client_id: import.meta.env.VITE_OIDC_CLIENT_ID,
|
|
27
|
+
scope: 'openid profile offline_access',
|
|
28
|
+
automaticSilentRenew: false,
|
|
29
|
+
},
|
|
30
|
+
web: {
|
|
31
|
+
settings: {
|
|
32
|
+
redirect_uri: `${window.location.origin}/callback`,
|
|
33
|
+
post_logout_redirect_uri: `${window.location.origin}/logout-callback`,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
native: {
|
|
37
|
+
settings: {
|
|
38
|
+
redirect_uri: 'capacitor-oidc-example:/callback',
|
|
39
|
+
post_logout_redirect_uri: 'capacitor-oidc-example:/logout-callback',
|
|
40
|
+
},
|
|
41
|
+
options: { storageNamespace: 'react-example' },
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export function getUserManager() {
|
|
46
|
+
return manager;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let callback: Promise<void> | undefined;
|
|
50
|
+
|
|
51
|
+
export function completeWebCallback(auth: CapacitorUserManager) {
|
|
52
|
+
callback ??= (async () => {
|
|
53
|
+
if (window.location.pathname === '/callback') {
|
|
54
|
+
await auth.signinCallback();
|
|
55
|
+
window.history.replaceState({}, '', '/');
|
|
56
|
+
} else if (window.location.pathname === '/logout-callback') {
|
|
57
|
+
await auth.signoutCallback();
|
|
58
|
+
window.history.replaceState({}, '', '/');
|
|
59
|
+
}
|
|
60
|
+
})();
|
|
61
|
+
|
|
62
|
+
return callback;
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The runnable example supplies local Keycloak defaults and keeps automatic
|
|
67
|
+
renewal off so its 30-second test tokens can be renewed with the example's
|
|
68
|
+
button. For a normal provider, enable automatic renewal and keep its
|
|
69
|
+
expiry-notification threshold below the provider's access-token lifetime. Set
|
|
70
|
+
the two Vite environment variables for your provider.
|
|
71
|
+
|
|
72
|
+
## Initialize from the application root
|
|
73
|
+
|
|
74
|
+
Complete browser callbacks, subscribe to user events, and restore the stored
|
|
75
|
+
user from the root component:
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
let active = true;
|
|
80
|
+
let auth: CapacitorUserManager | undefined;
|
|
81
|
+
const userLoaded = (user: User) => active && setUser(user);
|
|
82
|
+
const userUnloaded = () => active && setUser(null);
|
|
83
|
+
|
|
84
|
+
void getUserManager().then(async (manager) => {
|
|
85
|
+
if (!active) return;
|
|
86
|
+
auth = manager;
|
|
87
|
+
manager.events.addUserLoaded(userLoaded);
|
|
88
|
+
manager.events.addUserUnloaded(userUnloaded);
|
|
89
|
+
await completeWebCallback(manager);
|
|
90
|
+
const storedUser = await manager.getUser();
|
|
91
|
+
if (active) setUser(storedUser);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return () => {
|
|
95
|
+
active = false;
|
|
96
|
+
auth?.events.removeUserLoaded(userLoaded);
|
|
97
|
+
auth?.events.removeUserUnloaded(userUnloaded);
|
|
98
|
+
};
|
|
99
|
+
}, []);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Native callbacks complete inside the plugin and do not require React Router
|
|
103
|
+
routes.
|
|
104
|
+
|
|
105
|
+
## Bind actions
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
await auth.signin();
|
|
109
|
+
setUser(await auth.getValidUser(60));
|
|
110
|
+
await auth.signout();
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Retain the manager when React routes change. Call `dispose()` only when the
|
|
114
|
+
whole application is shutting down.
|
|
115
|
+
|
|
116
|
+
## Run the example
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
npm ci
|
|
120
|
+
npm run build
|
|
121
|
+
npm run e2e:keycloak:up
|
|
122
|
+
npm --prefix examples/react install
|
|
123
|
+
npm --prefix examples/react run dev
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Sign in with the disposable `demo` / `demo` account. Stop the local realm with
|
|
127
|
+
`npm run e2e:keycloak:down` when finished.
|
|
128
|
+
|
|
129
|
+
This local realm workflow is for the browser example. Android resolves
|
|
130
|
+
`localhost` inside the device or emulator, and native platforms should not use
|
|
131
|
+
the example's cleartext HTTP authority. Before building for iOS or Android, set
|
|
132
|
+
`VITE_OIDC_AUTHORITY` to an HTTPS authority reachable from that device and
|
|
133
|
+
register the configured native callback scheme at the provider. See
|
|
134
|
+
[iOS and Android setup](../PLATFORM_SETUP.md).
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Vue integration
|
|
2
|
+
|
|
3
|
+
The [Vue example](/examples/vue/) is a small Capacitor application with one
|
|
4
|
+
OIDC manager for the application lifetime. It restores the stored user when
|
|
5
|
+
Vue mounts, completes browser callbacks, and exposes sign-in, renewal, and
|
|
6
|
+
sign-out actions.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
npm install capacitor-oidc @capacitor/app
|
|
12
|
+
npx cap sync
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Create the manager once
|
|
16
|
+
|
|
17
|
+
Keep manager creation outside Vue components so route changes cannot create a
|
|
18
|
+
second manager:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
// src/auth.ts
|
|
22
|
+
import { CapacitorUserManager } from 'capacitor-oidc';
|
|
23
|
+
|
|
24
|
+
const manager = CapacitorUserManager.create({
|
|
25
|
+
common: {
|
|
26
|
+
authority: import.meta.env.VITE_OIDC_AUTHORITY,
|
|
27
|
+
client_id: import.meta.env.VITE_OIDC_CLIENT_ID,
|
|
28
|
+
scope: 'openid profile offline_access',
|
|
29
|
+
automaticSilentRenew: false,
|
|
30
|
+
},
|
|
31
|
+
web: {
|
|
32
|
+
settings: {
|
|
33
|
+
redirect_uri: `${window.location.origin}/callback`,
|
|
34
|
+
post_logout_redirect_uri: `${window.location.origin}/logout-callback`,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
native: {
|
|
38
|
+
settings: {
|
|
39
|
+
redirect_uri: 'capacitor-oidc-example:/callback',
|
|
40
|
+
post_logout_redirect_uri: 'capacitor-oidc-example:/logout-callback',
|
|
41
|
+
},
|
|
42
|
+
options: { storageNamespace: 'vue-example' },
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export function getUserManager() {
|
|
47
|
+
return manager;
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The runnable example supplies local Keycloak defaults as a convenience and
|
|
52
|
+
keeps automatic renewal off so its 30-second test tokens can be renewed with
|
|
53
|
+
the example's button. For a normal provider, enable automatic renewal and keep
|
|
54
|
+
its expiry-notification threshold below the provider's access-token lifetime.
|
|
55
|
+
Use environment variables for your provider in an application.
|
|
56
|
+
|
|
57
|
+
## Restore state and handle callbacks
|
|
58
|
+
|
|
59
|
+
Initialize authentication from the root component. Browser callback routes
|
|
60
|
+
must be handled before rendering the restored user:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
const auth = await getUserManager();
|
|
64
|
+
|
|
65
|
+
if (window.location.pathname === '/callback') {
|
|
66
|
+
await auth.signinCallback();
|
|
67
|
+
window.history.replaceState({}, '', '/');
|
|
68
|
+
} else if (window.location.pathname === '/logout-callback') {
|
|
69
|
+
await auth.signoutCallback();
|
|
70
|
+
window.history.replaceState({}, '', '/');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
user.value = await auth.getUser();
|
|
74
|
+
|
|
75
|
+
auth.events.addUserLoaded((loadedUser) => {
|
|
76
|
+
user.value = loadedUser;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
auth.events.addUserUnloaded(() => {
|
|
80
|
+
user.value = null;
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Native callbacks are completed by the plugin and do not need Vue Router
|
|
85
|
+
routes.
|
|
86
|
+
|
|
87
|
+
## Bind actions
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
await auth.signin();
|
|
91
|
+
user.value = await auth.getValidUser(60);
|
|
92
|
+
await auth.signout();
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Call `dispose()` only when the application is shutting down, not whenever a
|
|
96
|
+
route component unmounts.
|
|
97
|
+
|
|
98
|
+
## Run the example
|
|
99
|
+
|
|
100
|
+
Start the repository's local Keycloak realm, then start the Vue app:
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
npm ci
|
|
104
|
+
npm run build
|
|
105
|
+
npm run e2e:keycloak:up
|
|
106
|
+
npm --prefix examples/vue install
|
|
107
|
+
npm --prefix examples/vue run dev
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Sign in with the disposable `demo` / `demo` account. Stop the local realm with
|
|
111
|
+
`npm run e2e:keycloak:down` when finished.
|
|
112
|
+
|
|
113
|
+
This local realm workflow is for the browser example. Android resolves
|
|
114
|
+
`localhost` inside the device or emulator, and native platforms should not use
|
|
115
|
+
the example's cleartext HTTP authority. Before building for iOS or Android, set
|
|
116
|
+
`VITE_OIDC_AUTHORITY` to an HTTPS authority reachable from that device and
|
|
117
|
+
register the configured native callback scheme at the provider. See
|
|
118
|
+
[iOS and Android setup](../PLATFORM_SETUP.md).
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "capacitor-oidc",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "OAuth 2.0 and OpenID Connect authentication for Capacitor web, iOS, and Android, powered by oidc-client-ts.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"capacitor",
|
|
7
7
|
"capacitor-plugin",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"openid-connect",
|
|
12
12
|
"oidc",
|
|
13
13
|
"pkce",
|
|
14
|
+
"web",
|
|
14
15
|
"ios",
|
|
15
16
|
"android",
|
|
16
17
|
"oidc-client-ts"
|
|
@@ -48,6 +49,9 @@
|
|
|
48
49
|
"build": "npm run clean && tsc && rollup -c rollup.config.mjs",
|
|
49
50
|
"check:package": "node -e \"import('./dist/esm/index.js')\"",
|
|
50
51
|
"clean": "rimraf dist",
|
|
52
|
+
"docs:build": "vitepress build . && cp LICENSE .vitepress/dist/LICENSE",
|
|
53
|
+
"docs:dev": "vitepress dev .",
|
|
54
|
+
"docs:preview": "vitepress preview .",
|
|
51
55
|
"e2e:keycloak:down": "docker compose -f test/e2e/keycloak/compose.yml down",
|
|
52
56
|
"e2e:keycloak:up": "test/e2e/keycloak/create-certificate.sh test/e2e/keycloak/.certificates && docker compose -f test/e2e/keycloak/compose.yml up -d",
|
|
53
57
|
"example:build": "npm --prefix example run build",
|
|
@@ -78,6 +82,8 @@
|
|
|
78
82
|
"rimraf": "6.1.0",
|
|
79
83
|
"rollup": "4.62.5",
|
|
80
84
|
"typescript": "5.9.3",
|
|
85
|
+
"vite": "7.3.6",
|
|
86
|
+
"vitepress": "2.0.0-alpha.19",
|
|
81
87
|
"vitest": "3.2.7"
|
|
82
88
|
},
|
|
83
89
|
"prettier": "@ionic/prettier-config",
|