authfyio-expo 0.2.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 +21 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +84 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# authfyio-expo
|
|
2
|
+
|
|
3
|
+
Expo + React Native client for Authfyio — secure token storage, auth hooks, deep-link OAuth callbacks.
|
|
4
|
+
|
|
5
|
+
> Part of [Authfyio](https://authfyio.com) — a self-hostable authentication platform.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install authfyio-expo
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
See the full guide at **https://authfyio.com/docs**.
|
|
16
|
+
|
|
17
|
+
Point the SDK at your Authfyio instance via the same-origin proxy (`/api/af`) or set `AF_API_BASE_URL` for server-side calls.
|
|
18
|
+
|
|
19
|
+
## License
|
|
20
|
+
|
|
21
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expo + React Native client for Authfyio. React Native has no cookie jar,
|
|
3
|
+
* so we persist the session JWT in `expo-secure-store` (Keychain / Keystore)
|
|
4
|
+
* and attach it manually as `Authorization: Bearer`. OAuth sign-in uses deep
|
|
5
|
+
* links — register `yourapp://oauth-callback` in your Authfyio dashboard,
|
|
6
|
+
* catch it with `expo-linking`, and pass the `__session` JWT through.
|
|
7
|
+
*/
|
|
8
|
+
export type ExpoSessionClaims = {
|
|
9
|
+
sid: string;
|
|
10
|
+
sub: string;
|
|
11
|
+
env: string;
|
|
12
|
+
org?: string;
|
|
13
|
+
org_role?: string;
|
|
14
|
+
iat?: number;
|
|
15
|
+
exp?: number;
|
|
16
|
+
};
|
|
17
|
+
export type SecureStoreLike = {
|
|
18
|
+
getItemAsync(key: string): Promise<string | null>;
|
|
19
|
+
setItemAsync(key: string, value: string): Promise<void>;
|
|
20
|
+
deleteItemAsync(key: string): Promise<void>;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Hosted Authfyio SaaS URL. Expo apps don't share a cookie jar with a
|
|
24
|
+
* web origin, so we hit api.authfyio.com directly with bearer auth —
|
|
25
|
+
* no proxy needed. Override only when self-hosting.
|
|
26
|
+
*/
|
|
27
|
+
export declare const DEFAULT_API_BASE_URL = "https://api.authfyio.com";
|
|
28
|
+
export type AuthfyioExpoOptions = {
|
|
29
|
+
/**
|
|
30
|
+
* Base URL of the Authfyio instance API. Defaults to
|
|
31
|
+
* `https://api.authfyio.com` (the hosted SaaS).
|
|
32
|
+
*/
|
|
33
|
+
baseUrl?: string;
|
|
34
|
+
/** `expo-secure-store` (or any compatible adapter). If omitted, reads from
|
|
35
|
+
* memory — fine for dev, DO NOT use in production on-device. */
|
|
36
|
+
secureStore?: SecureStoreLike;
|
|
37
|
+
/** Storage key under which the JWT lives. Default `authfyio.jwt`. */
|
|
38
|
+
storageKey?: string;
|
|
39
|
+
};
|
|
40
|
+
export declare class AuthfyioExpoClient {
|
|
41
|
+
private readonly baseUrl;
|
|
42
|
+
private readonly store;
|
|
43
|
+
private readonly key;
|
|
44
|
+
constructor(opts?: AuthfyioExpoOptions);
|
|
45
|
+
getToken(): Promise<string | null>;
|
|
46
|
+
clearToken(): Promise<void>;
|
|
47
|
+
signInEmailPassword(email: string, password: string): Promise<ExpoSessionClaims | null>;
|
|
48
|
+
authHeader(): Promise<Record<string, string>>;
|
|
49
|
+
/** Deep-link handler for OAuth callbacks. Extract `?jwt=` from the URL. */
|
|
50
|
+
ingestDeepLink(url: string): Promise<ExpoSessionClaims | null>;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAClD,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,6BAA6B,CAAC;AAE/D,MAAM,MAAM,mBAAmB,GAAG;IAChC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;qEACiE;IACjE,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkB;IACxC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;gBAEjB,IAAI,GAAE,mBAAwB;IAMpC,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAIlC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAmBvF,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAKnD,2EAA2E;IACrE,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;CAQrE"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expo + React Native client for Authfyio. React Native has no cookie jar,
|
|
3
|
+
* so we persist the session JWT in `expo-secure-store` (Keychain / Keystore)
|
|
4
|
+
* and attach it manually as `Authorization: Bearer`. OAuth sign-in uses deep
|
|
5
|
+
* links — register `yourapp://oauth-callback` in your Authfyio dashboard,
|
|
6
|
+
* catch it with `expo-linking`, and pass the `__session` JWT through.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Hosted Authfyio SaaS URL. Expo apps don't share a cookie jar with a
|
|
10
|
+
* web origin, so we hit api.authfyio.com directly with bearer auth —
|
|
11
|
+
* no proxy needed. Override only when self-hosting.
|
|
12
|
+
*/
|
|
13
|
+
export const DEFAULT_API_BASE_URL = 'https://api.authfyio.com';
|
|
14
|
+
export class AuthfyioExpoClient {
|
|
15
|
+
baseUrl;
|
|
16
|
+
store;
|
|
17
|
+
key;
|
|
18
|
+
constructor(opts = {}) {
|
|
19
|
+
this.baseUrl = (opts.baseUrl ?? DEFAULT_API_BASE_URL).replace(/\/+$/, '');
|
|
20
|
+
this.store = opts.secureStore ?? inMemoryStore();
|
|
21
|
+
this.key = opts.storageKey ?? 'authfyio.jwt';
|
|
22
|
+
}
|
|
23
|
+
async getToken() {
|
|
24
|
+
return (await this.store.getItemAsync(this.key)) ?? null;
|
|
25
|
+
}
|
|
26
|
+
async clearToken() {
|
|
27
|
+
await this.store.deleteItemAsync(this.key);
|
|
28
|
+
}
|
|
29
|
+
async signInEmailPassword(email, password) {
|
|
30
|
+
const res = await fetch(`${this.baseUrl}/v1/auth/sign-in/email-password`, {
|
|
31
|
+
method: 'POST',
|
|
32
|
+
headers: { 'content-type': 'application/json' },
|
|
33
|
+
body: JSON.stringify({ email, password }),
|
|
34
|
+
});
|
|
35
|
+
if (!res.ok)
|
|
36
|
+
return null;
|
|
37
|
+
// The API normally sets cookies; React Native won't persist them, so we
|
|
38
|
+
// round-trip through /sessions/refresh which returns the JWT in a header
|
|
39
|
+
// as a fallback (added by the Authfyio instance when the `x-af-client`
|
|
40
|
+
// header is set — this header opts RN clients into the bearer model).
|
|
41
|
+
const body = (await res.json());
|
|
42
|
+
if (body?.jwt) {
|
|
43
|
+
await this.store.setItemAsync(this.key, body.jwt);
|
|
44
|
+
return decodeJwt(body.jwt);
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
async authHeader() {
|
|
49
|
+
const token = await this.getToken();
|
|
50
|
+
return token ? { authorization: `Bearer ${token}` } : {};
|
|
51
|
+
}
|
|
52
|
+
/** Deep-link handler for OAuth callbacks. Extract `?jwt=` from the URL. */
|
|
53
|
+
async ingestDeepLink(url) {
|
|
54
|
+
const q = url.split('?')[1] ?? '';
|
|
55
|
+
const params = new URLSearchParams(q);
|
|
56
|
+
const jwt = params.get('jwt');
|
|
57
|
+
if (!jwt)
|
|
58
|
+
return null;
|
|
59
|
+
await this.store.setItemAsync(this.key, jwt);
|
|
60
|
+
return decodeJwt(jwt);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function inMemoryStore() {
|
|
64
|
+
let v = null;
|
|
65
|
+
return {
|
|
66
|
+
async getItemAsync() {
|
|
67
|
+
return v;
|
|
68
|
+
},
|
|
69
|
+
async setItemAsync(_k, val) {
|
|
70
|
+
v = val;
|
|
71
|
+
},
|
|
72
|
+
async deleteItemAsync() {
|
|
73
|
+
v = null;
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function decodeJwt(token) {
|
|
78
|
+
const [, payload] = token.split('.');
|
|
79
|
+
const b64 = payload.replace(/-/g, '+').replace(/_/g, '/');
|
|
80
|
+
const padded = b64 + '='.repeat((4 - (b64.length % 4)) % 4);
|
|
81
|
+
// Expo/RN has `global.atob` in SDK 51+; otherwise polyfill.
|
|
82
|
+
const json = typeof atob === 'function' ? atob(padded) : Buffer.from(padded, 'base64').toString('utf8');
|
|
83
|
+
return JSON.parse(json);
|
|
84
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "authfyio-expo",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Expo + React Native client for Authfyio — secure token storage, auth hooks, deep-link OAuth callbacks.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc -p tsconfig.build.json",
|
|
21
|
+
"typecheck": "tsc -p tsconfig.build.json --noEmit",
|
|
22
|
+
"prepublishOnly": "npm run build"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"authfyio-react": "^0.3.0",
|
|
26
|
+
"react": ">=18",
|
|
27
|
+
"expo-secure-store": "*"
|
|
28
|
+
},
|
|
29
|
+
"peerDependenciesMeta": {
|
|
30
|
+
"expo-secure-store": {
|
|
31
|
+
"optional": true
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"authfyio",
|
|
36
|
+
"auth",
|
|
37
|
+
"expo",
|
|
38
|
+
"react-native"
|
|
39
|
+
],
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://authfyio.com/docs",
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/authfyio/authfyio.git"
|
|
47
|
+
}
|
|
48
|
+
}
|