@logto/capacitor 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Silverhand
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # Logto JS Capacitor SDK
2
+ [![Version](https://img.shields.io/npm/v/@logto/capacitor)](https://www.npmjs.com/package/@logto/capacitor)
3
+ [![Build Status](https://github.com/logto-io/js/actions/workflows/main.yml/badge.svg)](https://github.com/logto-io/js/actions/workflows/main.yml)
4
+ [![Codecov](https://img.shields.io/codecov/c/github/logto-io/js)](https://app.codecov.io/gh/logto-io/js?branch=master)
5
+
6
+ The Logto JavaScript Capacitor SDK written in TypeScript.
7
+
8
+ ## Installation
9
+
10
+ ### Using npm
11
+
12
+ ```bash
13
+ npm install @logto/capacitor @capacitor/app @capacitor/browser @capacitor/preferences
14
+ ```
15
+
16
+ ### Using yarn
17
+
18
+ ```bash
19
+ yarn add @logto/capacitor @capacitor/app @capacitor/browser @capacitor/preferences
20
+ ```
21
+
22
+ ### Using pnpm
23
+
24
+ ```bash
25
+ pnpm add @logto/capacitor @capacitor/app @capacitor/browser @capacitor/preferences
26
+ ```
27
+
28
+ ## What is this and how does it work?
29
+
30
+ See [Integrate Logto in your application](https://docs.logto.io/docs/recipes/integrate-logto/) for more information.
31
+
32
+ ## Resources
33
+
34
+ [![Website](https://img.shields.io/badge/website-logto.io-8262F8.svg)](https://logto.io/)
35
+ [![Docs](https://img.shields.io/badge/docs-logto.io-green.svg)](https://docs.logto.io/sdk/JavaScript/browser/)
36
+ [![Discord](https://img.shields.io/discord/965845662535147551?logo=discord&logoColor=ffffff&color=7389D8&cacheSeconds=600)](https://discord.gg/UEPaF3j5e6)
package/lib/index.cjs ADDED
@@ -0,0 +1,155 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var app = require('@capacitor/app');
6
+ var browser = require('@capacitor/browser');
7
+ var preferences = require('@capacitor/preferences');
8
+ var LogtoBaseClient = require('@logto/browser');
9
+
10
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
+
12
+ var LogtoBaseClient__default = /*#__PURE__*/_interopDefault(LogtoBaseClient);
13
+
14
+ class CapacitorLogtoClient extends LogtoBaseClient__default.default {
15
+ constructor(config, capacitorConfig = {}) {
16
+ const { openOptions } = capacitorConfig;
17
+ super(config);
18
+ // Use the Capacitor Browser plugin to open the sign-in and sign-out pages
19
+ // since the default location assignment method will open the pages in a
20
+ // system browser. We need to open an in-app browser to be able to handle
21
+ // the redirects back to the app.
22
+ // https://capacitorjs.com/docs/apis/browser
23
+ this.adapter.navigate = async (url) => {
24
+ return browser.Browser.open({
25
+ url,
26
+ windowName: '_self',
27
+ presentationStyle: 'popover',
28
+ ...openOptions,
29
+ });
30
+ };
31
+ // Use the Capacitor Preferences plugin to store the tokens, which will
32
+ // fallback to localStorage for web builds.
33
+ // https://capacitorjs.com/docs/apis/preferences
34
+ this.adapter.storage = {
35
+ getItem: async (key) => {
36
+ const { value } = await preferences.Preferences.get({ key });
37
+ return value;
38
+ },
39
+ setItem: async (key, value) => {
40
+ await preferences.Preferences.set({ key, value });
41
+ },
42
+ removeItem: async (key) => {
43
+ await preferences.Preferences.remove({ key });
44
+ },
45
+ };
46
+ }
47
+ /**
48
+ * Start the sign-in flow with the specified redirect URI. The URI must be
49
+ * registered in the Logto Console.
50
+ *
51
+ * Remember to configure the correct [scheme or universal links](https://capacitorjs.com/docs/guides/deep-links)
52
+ * to ensure the app can be opened from the redirect URI.
53
+ *
54
+ * @param redirectUri The redirect URI that the user will be redirected to after the sign-in flow is completed.
55
+ * @param interactionMode The interaction mode to be used for the authorization request. Note it's not
56
+ * a part of the OIDC standard, but a Logto-specific extension. Defaults to `signIn`.
57
+ * @throws {@link LogtoClientError} If error happens during the sign-in flow or the user cancels the sign-in.
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * const client = new CapacitorLogtoClient({
62
+ * endpoint: 'https://your.logto.endpoint',
63
+ * appId: 'your-app-id',
64
+ * });
65
+ *
66
+ * await client.signIn('io.logto.example://callback'); // throws if error happens
67
+ * console.log(await client.getIdTokenClaims()); // { sub: '123', ... }
68
+ * ```
69
+ *
70
+ * @remarks
71
+ * The user will be redirected to that URI after the sign-in flow is completed,
72
+ * and the client will be able to get the authorization code from the URI.
73
+ * {@link handleSignInCallback} will be called after the user is redirected.
74
+ *
75
+ * @see {@link https://docs.logto.io/docs/recipes/integrate-logto/vanilla-js/#sign-in | Sign in} for more information.
76
+ * @see {@link InteractionMode}
77
+ */
78
+ async signIn(redirectUri, interactionMode) {
79
+ return new Promise((resolve, reject) => {
80
+ const run = async () => {
81
+ const [browserHandle, appHandle] = await Promise.all([
82
+ // Handle the case where the user closes the browser during the sign-in.
83
+ browser.Browser.addListener('browserFinished', async () => {
84
+ await Promise.all([browserHandle.remove(), appHandle.remove()]);
85
+ reject(new LogtoBaseClient.LogtoClientError('user_cancelled'));
86
+ }),
87
+ // Handle the case where the user completes the sign-in and is redirected
88
+ // back to the app.
89
+ app.App.addListener('appUrlOpen', async ({ url }) => {
90
+ if (!url.startsWith(redirectUri)) {
91
+ return;
92
+ }
93
+ await Promise.all([
94
+ // One last step of the sign-in flow
95
+ this.handleSignInCallback(url),
96
+ // Close the browser and remove the listeners
97
+ browser.Browser.close(),
98
+ browserHandle.remove(),
99
+ appHandle.remove(),
100
+ ]);
101
+ resolve();
102
+ }),
103
+ // Open the in-app browser to start the sign-in flow
104
+ super.signIn(redirectUri, interactionMode),
105
+ ]);
106
+ };
107
+ void run();
108
+ });
109
+ }
110
+ /**
111
+ * Start the sign-out flow with the specified redirect URI. The URI must be
112
+ * registered in the Logto Console.
113
+ *
114
+ * It will also revoke all the tokens and clean up the storage.
115
+ *
116
+ * - If the `postLogoutRedirectUri` is not specified, the user will see a default
117
+ * page after the sign-out flow is completed, they need to close the browser
118
+ * manually to return to the app.
119
+ * - If the `postLogoutRedirectUri` is specified, the user will be redirected to
120
+ * that URI after the sign-out flow is completed. Remember to configure the correct
121
+ * [scheme or universal links](https://capacitorjs.com/docs/guides/deep-links)
122
+ * to ensure the app can be opened from the redirect URI.
123
+ *
124
+ * @param postLogoutRedirectUri The URI that the user will be redirected to after the sign-out flow is completed.
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * await client.signOut('io.logto.example://callback');
129
+ * ```
130
+ */
131
+ async signOut(postLogoutRedirectUri) {
132
+ return new Promise((resolve) => {
133
+ const run = async () => {
134
+ const [handle] = await Promise.all([
135
+ postLogoutRedirectUri
136
+ ? app.App.addListener('appUrlOpen', async ({ url }) => {
137
+ if (postLogoutRedirectUri && !url.startsWith(postLogoutRedirectUri)) {
138
+ return;
139
+ }
140
+ await Promise.all([browser.Browser.close(), handle.remove()]);
141
+ resolve();
142
+ })
143
+ : browser.Browser.addListener('browserFinished', async () => {
144
+ await handle.remove();
145
+ resolve();
146
+ }),
147
+ super.signOut(postLogoutRedirectUri),
148
+ ]);
149
+ };
150
+ void run();
151
+ });
152
+ }
153
+ }
154
+
155
+ exports.default = CapacitorLogtoClient;
package/lib/index.d.ts ADDED
@@ -0,0 +1,66 @@
1
+ import { type OpenOptions } from '@capacitor/browser';
2
+ import LogtoBaseClient, { type InteractionMode, type LogtoConfig } from '@logto/browser';
3
+ export type CapacitorConfig = {
4
+ /**
5
+ * The options to pass to the `open` method of the Capacitor Browser plugin.
6
+ * @default { windowName: '_self', presentationStyle: 'popover' }
7
+ */
8
+ openOptions?: OpenOptions;
9
+ };
10
+ export default class CapacitorLogtoClient extends LogtoBaseClient {
11
+ constructor(config: LogtoConfig, capacitorConfig?: CapacitorConfig);
12
+ /**
13
+ * Start the sign-in flow with the specified redirect URI. The URI must be
14
+ * registered in the Logto Console.
15
+ *
16
+ * Remember to configure the correct [scheme or universal links](https://capacitorjs.com/docs/guides/deep-links)
17
+ * to ensure the app can be opened from the redirect URI.
18
+ *
19
+ * @param redirectUri The redirect URI that the user will be redirected to after the sign-in flow is completed.
20
+ * @param interactionMode The interaction mode to be used for the authorization request. Note it's not
21
+ * a part of the OIDC standard, but a Logto-specific extension. Defaults to `signIn`.
22
+ * @throws {@link LogtoClientError} If error happens during the sign-in flow or the user cancels the sign-in.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * const client = new CapacitorLogtoClient({
27
+ * endpoint: 'https://your.logto.endpoint',
28
+ * appId: 'your-app-id',
29
+ * });
30
+ *
31
+ * await client.signIn('io.logto.example://callback'); // throws if error happens
32
+ * console.log(await client.getIdTokenClaims()); // { sub: '123', ... }
33
+ * ```
34
+ *
35
+ * @remarks
36
+ * The user will be redirected to that URI after the sign-in flow is completed,
37
+ * and the client will be able to get the authorization code from the URI.
38
+ * {@link handleSignInCallback} will be called after the user is redirected.
39
+ *
40
+ * @see {@link https://docs.logto.io/docs/recipes/integrate-logto/vanilla-js/#sign-in | Sign in} for more information.
41
+ * @see {@link InteractionMode}
42
+ */
43
+ signIn(redirectUri: string, interactionMode?: InteractionMode): Promise<void>;
44
+ /**
45
+ * Start the sign-out flow with the specified redirect URI. The URI must be
46
+ * registered in the Logto Console.
47
+ *
48
+ * It will also revoke all the tokens and clean up the storage.
49
+ *
50
+ * - If the `postLogoutRedirectUri` is not specified, the user will see a default
51
+ * page after the sign-out flow is completed, they need to close the browser
52
+ * manually to return to the app.
53
+ * - If the `postLogoutRedirectUri` is specified, the user will be redirected to
54
+ * that URI after the sign-out flow is completed. Remember to configure the correct
55
+ * [scheme or universal links](https://capacitorjs.com/docs/guides/deep-links)
56
+ * to ensure the app can be opened from the redirect URI.
57
+ *
58
+ * @param postLogoutRedirectUri The URI that the user will be redirected to after the sign-out flow is completed.
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * await client.signOut('io.logto.example://callback');
63
+ * ```
64
+ */
65
+ signOut(postLogoutRedirectUri?: string): Promise<void>;
66
+ }
package/lib/index.js ADDED
@@ -0,0 +1,147 @@
1
+ import { App } from '@capacitor/app';
2
+ import { Browser } from '@capacitor/browser';
3
+ import { Preferences } from '@capacitor/preferences';
4
+ import LogtoBaseClient, { LogtoClientError } from '@logto/browser';
5
+
6
+ class CapacitorLogtoClient extends LogtoBaseClient {
7
+ constructor(config, capacitorConfig = {}) {
8
+ const { openOptions } = capacitorConfig;
9
+ super(config);
10
+ // Use the Capacitor Browser plugin to open the sign-in and sign-out pages
11
+ // since the default location assignment method will open the pages in a
12
+ // system browser. We need to open an in-app browser to be able to handle
13
+ // the redirects back to the app.
14
+ // https://capacitorjs.com/docs/apis/browser
15
+ this.adapter.navigate = async (url) => {
16
+ return Browser.open({
17
+ url,
18
+ windowName: '_self',
19
+ presentationStyle: 'popover',
20
+ ...openOptions,
21
+ });
22
+ };
23
+ // Use the Capacitor Preferences plugin to store the tokens, which will
24
+ // fallback to localStorage for web builds.
25
+ // https://capacitorjs.com/docs/apis/preferences
26
+ this.adapter.storage = {
27
+ getItem: async (key) => {
28
+ const { value } = await Preferences.get({ key });
29
+ return value;
30
+ },
31
+ setItem: async (key, value) => {
32
+ await Preferences.set({ key, value });
33
+ },
34
+ removeItem: async (key) => {
35
+ await Preferences.remove({ key });
36
+ },
37
+ };
38
+ }
39
+ /**
40
+ * Start the sign-in flow with the specified redirect URI. The URI must be
41
+ * registered in the Logto Console.
42
+ *
43
+ * Remember to configure the correct [scheme or universal links](https://capacitorjs.com/docs/guides/deep-links)
44
+ * to ensure the app can be opened from the redirect URI.
45
+ *
46
+ * @param redirectUri The redirect URI that the user will be redirected to after the sign-in flow is completed.
47
+ * @param interactionMode The interaction mode to be used for the authorization request. Note it's not
48
+ * a part of the OIDC standard, but a Logto-specific extension. Defaults to `signIn`.
49
+ * @throws {@link LogtoClientError} If error happens during the sign-in flow or the user cancels the sign-in.
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * const client = new CapacitorLogtoClient({
54
+ * endpoint: 'https://your.logto.endpoint',
55
+ * appId: 'your-app-id',
56
+ * });
57
+ *
58
+ * await client.signIn('io.logto.example://callback'); // throws if error happens
59
+ * console.log(await client.getIdTokenClaims()); // { sub: '123', ... }
60
+ * ```
61
+ *
62
+ * @remarks
63
+ * The user will be redirected to that URI after the sign-in flow is completed,
64
+ * and the client will be able to get the authorization code from the URI.
65
+ * {@link handleSignInCallback} will be called after the user is redirected.
66
+ *
67
+ * @see {@link https://docs.logto.io/docs/recipes/integrate-logto/vanilla-js/#sign-in | Sign in} for more information.
68
+ * @see {@link InteractionMode}
69
+ */
70
+ async signIn(redirectUri, interactionMode) {
71
+ return new Promise((resolve, reject) => {
72
+ const run = async () => {
73
+ const [browserHandle, appHandle] = await Promise.all([
74
+ // Handle the case where the user closes the browser during the sign-in.
75
+ Browser.addListener('browserFinished', async () => {
76
+ await Promise.all([browserHandle.remove(), appHandle.remove()]);
77
+ reject(new LogtoClientError('user_cancelled'));
78
+ }),
79
+ // Handle the case where the user completes the sign-in and is redirected
80
+ // back to the app.
81
+ App.addListener('appUrlOpen', async ({ url }) => {
82
+ if (!url.startsWith(redirectUri)) {
83
+ return;
84
+ }
85
+ await Promise.all([
86
+ // One last step of the sign-in flow
87
+ this.handleSignInCallback(url),
88
+ // Close the browser and remove the listeners
89
+ Browser.close(),
90
+ browserHandle.remove(),
91
+ appHandle.remove(),
92
+ ]);
93
+ resolve();
94
+ }),
95
+ // Open the in-app browser to start the sign-in flow
96
+ super.signIn(redirectUri, interactionMode),
97
+ ]);
98
+ };
99
+ void run();
100
+ });
101
+ }
102
+ /**
103
+ * Start the sign-out flow with the specified redirect URI. The URI must be
104
+ * registered in the Logto Console.
105
+ *
106
+ * It will also revoke all the tokens and clean up the storage.
107
+ *
108
+ * - If the `postLogoutRedirectUri` is not specified, the user will see a default
109
+ * page after the sign-out flow is completed, they need to close the browser
110
+ * manually to return to the app.
111
+ * - If the `postLogoutRedirectUri` is specified, the user will be redirected to
112
+ * that URI after the sign-out flow is completed. Remember to configure the correct
113
+ * [scheme or universal links](https://capacitorjs.com/docs/guides/deep-links)
114
+ * to ensure the app can be opened from the redirect URI.
115
+ *
116
+ * @param postLogoutRedirectUri The URI that the user will be redirected to after the sign-out flow is completed.
117
+ *
118
+ * @example
119
+ * ```ts
120
+ * await client.signOut('io.logto.example://callback');
121
+ * ```
122
+ */
123
+ async signOut(postLogoutRedirectUri) {
124
+ return new Promise((resolve) => {
125
+ const run = async () => {
126
+ const [handle] = await Promise.all([
127
+ postLogoutRedirectUri
128
+ ? App.addListener('appUrlOpen', async ({ url }) => {
129
+ if (postLogoutRedirectUri && !url.startsWith(postLogoutRedirectUri)) {
130
+ return;
131
+ }
132
+ await Promise.all([Browser.close(), handle.remove()]);
133
+ resolve();
134
+ })
135
+ : Browser.addListener('browserFinished', async () => {
136
+ await handle.remove();
137
+ resolve();
138
+ }),
139
+ super.signOut(postLogoutRedirectUri),
140
+ ]);
141
+ };
142
+ void run();
143
+ });
144
+ }
145
+ }
146
+
147
+ export { CapacitorLogtoClient as default };
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@logto/capacitor",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "main": "./lib/index.cjs",
6
+ "module": "./lib/index.js",
7
+ "types": "./lib/index.d.ts",
8
+ "exports": {
9
+ "types": "./lib/index.d.ts",
10
+ "require": "./lib/index.cjs",
11
+ "import": "./lib/index.js",
12
+ "default": "./lib/index.js"
13
+ },
14
+ "files": [
15
+ "lib"
16
+ ],
17
+ "license": "MIT",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/logto-io/js.git",
21
+ "directory": "packages/capacitor"
22
+ },
23
+ "dependencies": {
24
+ "@logto/browser": "^2.1.1"
25
+ },
26
+ "devDependencies": {
27
+ "@capacitor/app": "^5.0.6",
28
+ "@capacitor/browser": "^5.0.6",
29
+ "@capacitor/preferences": "^5.0.6",
30
+ "@silverhand/eslint-config": "^4.0.1",
31
+ "@silverhand/ts-config": "^4.0.0",
32
+ "@swc/core": "^1.3.50",
33
+ "@swc/jest": "^0.2.24",
34
+ "@types/jest": "^29.5.0",
35
+ "eslint": "^8.44.0",
36
+ "jest": "^29.5.0",
37
+ "jest-environment-jsdom": "^29.5.0",
38
+ "jest-matcher-specific-error": "^1.0.0",
39
+ "lint-staged": "^13.0.0",
40
+ "prettier": "^3.0.0",
41
+ "text-encoder": "^0.0.4",
42
+ "typescript": "^5.0.0"
43
+ },
44
+ "eslintConfig": {
45
+ "extends": "@silverhand"
46
+ },
47
+ "prettier": "@silverhand/eslint-config/.prettierrc",
48
+ "publishConfig": {
49
+ "access": "public"
50
+ },
51
+ "peerDependencies": {
52
+ "@capacitor/app": "^5.0.6",
53
+ "@capacitor/browser": "^5.0.6",
54
+ "@capacitor/preferences": "^5.0.6"
55
+ },
56
+ "scripts": {
57
+ "dev:tsc": "tsc -p tsconfig.build.json -w --preserveWatchOutput",
58
+ "precommit": "lint-staged",
59
+ "check": "tsc --noEmit",
60
+ "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c",
61
+ "lint": "eslint --ext .ts src",
62
+ "test": "jest",
63
+ "test:coverage": "jest --silent --coverage"
64
+ }
65
+ }