phonelink 0.1.0 → 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/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# phonelink
|
|
2
|
+
|
|
3
|
+
Phone number verification for Expo and React Native apps. Phonelink handles the entire verification flow — your app opens a secure browser session, the user verifies their phone number, and your server validates the resulting JWT.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install phonelink
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Expo peer dependencies
|
|
12
|
+
|
|
13
|
+
The client-side module requires `expo-crypto` and `expo-web-browser`:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx expo install expo-crypto expo-web-browser
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
### 1. Client — start the verification flow
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { phonelink } from "phonelink/expo";
|
|
25
|
+
|
|
26
|
+
const result = await phonelink.verify("your-client-id");
|
|
27
|
+
|
|
28
|
+
if (result) {
|
|
29
|
+
// Send result.token and result.nonce to your backend
|
|
30
|
+
await fetch("/api/verify-phone", {
|
|
31
|
+
method: "POST",
|
|
32
|
+
body: JSON.stringify({ token: result.token, nonce: result.nonce }),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`phonelink.verify` opens a secure in-app browser where the user completes phone verification. It returns `{ token, nonce }` on success, or `null` if the user cancels.
|
|
38
|
+
|
|
39
|
+
| Parameter | Type | Description |
|
|
40
|
+
|---------------|----------|-------------------------------------------------------|
|
|
41
|
+
| `clientId` | `string` | Your Phonelink client ID |
|
|
42
|
+
| `redirectUrl` | `string` | Deep link URI for your app (default `phonelink://verify`) |
|
|
43
|
+
|
|
44
|
+
### 2. Server — validate the token
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { verifyPhonelinkToken } from "phonelink/server";
|
|
48
|
+
|
|
49
|
+
const payload = await verifyPhonelinkToken(token, nonce, "your-client-id");
|
|
50
|
+
|
|
51
|
+
console.log(payload.phone_e164); // "+14155551234"
|
|
52
|
+
console.log(payload.verified); // true
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`verifyPhonelinkToken` verifies the JWT signature against the Phonelink JWKS endpoint, checks the issuer and audience, and validates the nonce and verification status. It throws if anything is invalid.
|
|
56
|
+
|
|
57
|
+
| Parameter | Type | Description |
|
|
58
|
+
|-----------------|----------|--------------------------------------------|
|
|
59
|
+
| `token` | `string` | The JWT returned from the client flow |
|
|
60
|
+
| `expectedNonce` | `string` | The nonce returned alongside the token |
|
|
61
|
+
| `expectedAud` | `string` | Your client ID (must match the token audience) |
|
|
62
|
+
|
|
63
|
+
## Payload
|
|
64
|
+
|
|
65
|
+
The verified token resolves to a `PhonelinkPayload`:
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
interface PhonelinkPayload {
|
|
69
|
+
phone_e164: string; // E.164 formatted phone number
|
|
70
|
+
verified: boolean; // Whether the phone number was verified
|
|
71
|
+
method: string; // Verification method used
|
|
72
|
+
provider: string; // Verification provider
|
|
73
|
+
nonce: string; // Nonce for replay protection
|
|
74
|
+
sub: string; // Subject identifier
|
|
75
|
+
iss: string; // Issuer
|
|
76
|
+
aud: string; // Audience (your client ID)
|
|
77
|
+
iat: number; // Issued at (unix timestamp)
|
|
78
|
+
exp: number; // Expiry (unix timestamp)
|
|
79
|
+
jti: string; // Unique token identifier
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Security
|
|
84
|
+
|
|
85
|
+
- Tokens are signed JWTs verified against the Phonelink JWKS endpoint
|
|
86
|
+
- Nonce validation prevents replay attacks — always forward the nonce from the client and check it on the server
|
|
87
|
+
- The audience claim is checked to ensure the token was issued for your application
|
|
88
|
+
- Token expiry is enforced automatically by the JWT verification
|
|
89
|
+
|
|
90
|
+
## License
|
|
91
|
+
|
|
92
|
+
MIT
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/shared/constants.ts
|
|
2
|
-
var PHONELINK_URL = "https://
|
|
3
|
-
var EXPECTED_ISSUER = "https://
|
|
2
|
+
var PHONELINK_URL = "https://phone.link/auth";
|
|
3
|
+
var EXPECTED_ISSUER = "https://phone.link";
|
|
4
4
|
function buildAuthUrl(clientId, redirectUrl, nonce) {
|
|
5
5
|
return `${PHONELINK_URL}?client_id=${encodeURIComponent(clientId)}&redirect_url=${encodeURIComponent(redirectUrl)}&nonce=${nonce}`;
|
|
6
6
|
}
|
package/dist/expo/index.js
CHANGED
|
@@ -37,7 +37,7 @@ var Crypto = __toESM(require("expo-crypto"));
|
|
|
37
37
|
var WebBrowser = __toESM(require("expo-web-browser"));
|
|
38
38
|
|
|
39
39
|
// src/shared/constants.ts
|
|
40
|
-
var PHONELINK_URL = "https://
|
|
40
|
+
var PHONELINK_URL = "https://phone.link/auth";
|
|
41
41
|
function buildAuthUrl(clientId, redirectUrl, nonce) {
|
|
42
42
|
return `${PHONELINK_URL}?client_id=${encodeURIComponent(clientId)}&redirect_url=${encodeURIComponent(redirectUrl)}&nonce=${nonce}`;
|
|
43
43
|
}
|
package/dist/expo/index.mjs
CHANGED
package/dist/server/index.js
CHANGED
|
@@ -26,11 +26,11 @@ module.exports = __toCommonJS(server_exports);
|
|
|
26
26
|
var import_jose = require("jose");
|
|
27
27
|
|
|
28
28
|
// src/shared/constants.ts
|
|
29
|
-
var EXPECTED_ISSUER = "https://
|
|
29
|
+
var EXPECTED_ISSUER = "https://phone.link";
|
|
30
30
|
|
|
31
31
|
// src/server/index.ts
|
|
32
32
|
var JWKS = (0, import_jose.createRemoteJWKSet)(
|
|
33
|
-
new URL("https://
|
|
33
|
+
new URL("https://phone.link/.well-known/jwks.json")
|
|
34
34
|
);
|
|
35
35
|
async function verifyPhonelinkToken(token, expectedNonce, expectedAud) {
|
|
36
36
|
const { payload } = await (0, import_jose.jwtVerify)(token, JWKS, {
|
package/dist/server/index.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
EXPECTED_ISSUER
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-7HCSQUU2.mjs";
|
|
4
4
|
|
|
5
5
|
// src/server/index.ts
|
|
6
6
|
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
7
7
|
var JWKS = createRemoteJWKSet(
|
|
8
|
-
new URL("https://
|
|
8
|
+
new URL("https://phone.link/.well-known/jwks.json")
|
|
9
9
|
);
|
|
10
10
|
async function verifyPhonelinkToken(token, expectedNonce, expectedAud) {
|
|
11
11
|
const { payload } = await jwtVerify(token, JWKS, {
|