@glideidentity/glide-be-sdk-node 2.0.1 → 2.1.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/dist/device-binding.d.ts +110 -0
- package/dist/device-binding.d.ts.map +1 -0
- package/dist/device-binding.js +321 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +0 -9
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +3 -1
- package/dist/services/magical-auth.d.ts +33 -4
- package/dist/services/magical-auth.d.ts.map +1 -1
- package/dist/services/magical-auth.js +145 -22
- package/dist/types.d.ts +19 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/validation.d.ts +0 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +3 -6
- package/package.json +12 -3
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/** Cookie name prefix for device binding codes. Append the session key for the full name. */
|
|
2
|
+
export declare const BINDING_COOKIE_PREFIX = "_glide_bind_";
|
|
3
|
+
/** Cookie max-age in seconds (10 minutes, matches session TTL). */
|
|
4
|
+
export declare const BINDING_COOKIE_MAX_AGE = 600;
|
|
5
|
+
/**
|
|
6
|
+
* Returns the full cookie name for a given session key.
|
|
7
|
+
*
|
|
8
|
+
* Using a session-scoped name (`_glide_bind_{sessionKey}`) ensures that
|
|
9
|
+
* parallel `prepare()` calls from the same browser don't overwrite each
|
|
10
|
+
* other's cookies — each session gets its own cookie.
|
|
11
|
+
*/
|
|
12
|
+
export declare function getBindingCookieName(sessionKey: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Regex matching a valid 64-character hex string (case-insensitive).
|
|
15
|
+
*
|
|
16
|
+
* Accepts both lowercase and uppercase hex so that external inputs (URL
|
|
17
|
+
* fragments, cookie values) still pass validation even if a proxy or
|
|
18
|
+
* browser extension uppercases them. Internal code normalises to
|
|
19
|
+
* lowercase before hashing or sending to the aggregator.
|
|
20
|
+
*/
|
|
21
|
+
export declare const HEX_64: RegExp;
|
|
22
|
+
/**
|
|
23
|
+
* Asserts that the given value is a valid 64-character hex string.
|
|
24
|
+
* @throws {Error} If the value does not match.
|
|
25
|
+
*/
|
|
26
|
+
export declare function assertValidFeCode(feCode: string): void;
|
|
27
|
+
/**
|
|
28
|
+
* Generates a cryptographically random device binding code.
|
|
29
|
+
* @returns 64-character lowercase hex string (32 random bytes).
|
|
30
|
+
*/
|
|
31
|
+
export declare function generateFeCode(): string;
|
|
32
|
+
/**
|
|
33
|
+
* Computes the SHA-256 hash of a device binding code.
|
|
34
|
+
* @param feCode - The 64-character hex binding code (case-insensitive input, normalised to lowercase).
|
|
35
|
+
* @returns 64-character lowercase hex SHA-256 hash.
|
|
36
|
+
*/
|
|
37
|
+
export declare function computeFeHash(feCode: string): string;
|
|
38
|
+
export interface BindingCookieOptions {
|
|
39
|
+
/** Cookie domain. Must match the completion redirect URL domain. */
|
|
40
|
+
domain?: string;
|
|
41
|
+
/** Cookie path. Defaults to '/'. */
|
|
42
|
+
path?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Whether to set the Secure flag. Defaults to `true`.
|
|
45
|
+
*
|
|
46
|
+
* Set to `false` only for local development over plain HTTP.
|
|
47
|
+
* In production this MUST remain `true`.
|
|
48
|
+
*/
|
|
49
|
+
secure?: boolean;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Builds the Set-Cookie header value to store the device binding code.
|
|
53
|
+
*
|
|
54
|
+
* @param feCode - The binding code to store (64-char hex, normalised to lowercase).
|
|
55
|
+
* @param sessionKey - The session key — used in the cookie name to avoid collisions between parallel sessions.
|
|
56
|
+
* @param options - Optional domain/path overrides.
|
|
57
|
+
* @returns A complete Set-Cookie header value string.
|
|
58
|
+
*/
|
|
59
|
+
export declare function buildSetBindingCookieHeader(feCode: string, sessionKey: string, options?: BindingCookieOptions): string;
|
|
60
|
+
/**
|
|
61
|
+
* Builds the Set-Cookie header value to clear the device binding cookie.
|
|
62
|
+
*
|
|
63
|
+
* @param sessionKey - The session key — identifies which session's cookie to clear.
|
|
64
|
+
* @param options - Optional domain/path (must match the original cookie).
|
|
65
|
+
* @returns A complete Set-Cookie header value string that expires the cookie.
|
|
66
|
+
*/
|
|
67
|
+
export declare function buildClearBindingCookieHeader(sessionKey: string, options?: BindingCookieOptions): string;
|
|
68
|
+
/**
|
|
69
|
+
* Parses the device binding code from a raw Cookie header string for a specific session.
|
|
70
|
+
*
|
|
71
|
+
* Returns `undefined` if the cookie is not present or the value is not
|
|
72
|
+
* a valid 64-character hex string. When found, the value is normalised
|
|
73
|
+
* to lowercase for deterministic hashing downstream.
|
|
74
|
+
*
|
|
75
|
+
* @param cookieHeader - The raw `Cookie` header value (e.g., "foo=bar; _glide_bind_abc123=...").
|
|
76
|
+
* @param sessionKey - The session key that identifies the cookie to look for.
|
|
77
|
+
* @returns The fe_code value (lowercase), or undefined if the cookie is not present or invalid.
|
|
78
|
+
*/
|
|
79
|
+
export declare function parseBindingCookie(cookieHeader: string | undefined, sessionKey: string): string | undefined;
|
|
80
|
+
/**
|
|
81
|
+
* Builds Set-Cookie headers that expire all previous device binding cookies
|
|
82
|
+
* found in the request.
|
|
83
|
+
*
|
|
84
|
+
* Only one Link flow can be active per browser at a time (App Clip is a
|
|
85
|
+
* single native process), so stale cookies from abandoned flows must be
|
|
86
|
+
* cleared to prevent the old redirect from completing with an outdated
|
|
87
|
+
* binding code.
|
|
88
|
+
*
|
|
89
|
+
* @param cookieHeader - The raw `Cookie` request header.
|
|
90
|
+
* @param options - Cookie options (secure, domain, path) — must match the original set call.
|
|
91
|
+
* @returns Array of Set-Cookie header values that expire stale binding cookies.
|
|
92
|
+
*/
|
|
93
|
+
export declare function clearStaleBindingCookies(cookieHeader: string | undefined, options?: BindingCookieOptions): string[];
|
|
94
|
+
/**
|
|
95
|
+
* Returns the HTML for the completion redirect page.
|
|
96
|
+
*
|
|
97
|
+
* This page extracts `agg_code` and `session_key` from the URL fragment,
|
|
98
|
+
* POSTs them to the developer's backend (which auto-includes the
|
|
99
|
+
* `_glide_bind_{sessionKey}` HttpOnly cookie), and displays the result.
|
|
100
|
+
*
|
|
101
|
+
* On successful completion, a **localStorage signal** is written
|
|
102
|
+
* (`glide_signal_{session_key}`) so the FE SDK in the original tab can
|
|
103
|
+
* detect completion via `StorageEvent` without polling. The signal is
|
|
104
|
+
* automatically cleaned up after 5 seconds.
|
|
105
|
+
*
|
|
106
|
+
* @param completeEndpoint - The developer backend endpoint to POST to (e.g., "/api/glide/complete").
|
|
107
|
+
* @returns Complete HTML string ready to serve.
|
|
108
|
+
*/
|
|
109
|
+
export declare function getCompletionPageHtml(completeEndpoint: string): string;
|
|
110
|
+
//# sourceMappingURL=device-binding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-binding.d.ts","sourceRoot":"","sources":["../src/device-binding.ts"],"names":[],"mappings":"AAMA,6FAA6F;AAC7F,eAAO,MAAM,qBAAqB,iBAAiB,CAAC;AAEpD,mEAAmE;AACnE,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAK/D;AAMD;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,QAAsB,CAAC;AAE1C;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAItD;AAMD;;;GAGG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAIpD;AAMD,MAAM,WAAW,oBAAoB;IACjC,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAeD;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,MAAM,CAoBtH;AAED;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,MAAM,CAoBxG;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAU3G;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,MAAM,EAAE,CAmBnH;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAqGtE"}
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HEX_64 = exports.BINDING_COOKIE_MAX_AGE = exports.BINDING_COOKIE_PREFIX = void 0;
|
|
4
|
+
exports.getBindingCookieName = getBindingCookieName;
|
|
5
|
+
exports.assertValidFeCode = assertValidFeCode;
|
|
6
|
+
exports.generateFeCode = generateFeCode;
|
|
7
|
+
exports.computeFeHash = computeFeHash;
|
|
8
|
+
exports.buildSetBindingCookieHeader = buildSetBindingCookieHeader;
|
|
9
|
+
exports.buildClearBindingCookieHeader = buildClearBindingCookieHeader;
|
|
10
|
+
exports.parseBindingCookie = parseBindingCookie;
|
|
11
|
+
exports.clearStaleBindingCookies = clearStaleBindingCookies;
|
|
12
|
+
exports.getCompletionPageHtml = getCompletionPageHtml;
|
|
13
|
+
const crypto_1 = require("crypto");
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Constants
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
/** Cookie name prefix for device binding codes. Append the session key for the full name. */
|
|
18
|
+
exports.BINDING_COOKIE_PREFIX = '_glide_bind_';
|
|
19
|
+
/** Cookie max-age in seconds (10 minutes, matches session TTL). */
|
|
20
|
+
exports.BINDING_COOKIE_MAX_AGE = 600;
|
|
21
|
+
/**
|
|
22
|
+
* Returns the full cookie name for a given session key.
|
|
23
|
+
*
|
|
24
|
+
* Using a session-scoped name (`_glide_bind_{sessionKey}`) ensures that
|
|
25
|
+
* parallel `prepare()` calls from the same browser don't overwrite each
|
|
26
|
+
* other's cookies — each session gets its own cookie.
|
|
27
|
+
*/
|
|
28
|
+
function getBindingCookieName(sessionKey) {
|
|
29
|
+
if (!sessionKey || UNSAFE_COOKIE_ATTR.test(sessionKey) || sessionKey.includes('=')) {
|
|
30
|
+
throw new Error('Invalid sessionKey for cookie name: must not be empty or contain ";", "=", "\\r", or "\\n"');
|
|
31
|
+
}
|
|
32
|
+
return `${exports.BINDING_COOKIE_PREFIX}${sessionKey}`;
|
|
33
|
+
}
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Shared validation
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
/**
|
|
38
|
+
* Regex matching a valid 64-character hex string (case-insensitive).
|
|
39
|
+
*
|
|
40
|
+
* Accepts both lowercase and uppercase hex so that external inputs (URL
|
|
41
|
+
* fragments, cookie values) still pass validation even if a proxy or
|
|
42
|
+
* browser extension uppercases them. Internal code normalises to
|
|
43
|
+
* lowercase before hashing or sending to the aggregator.
|
|
44
|
+
*/
|
|
45
|
+
exports.HEX_64 = /^[0-9a-fA-F]{64}$/;
|
|
46
|
+
/**
|
|
47
|
+
* Asserts that the given value is a valid 64-character hex string.
|
|
48
|
+
* @throws {Error} If the value does not match.
|
|
49
|
+
*/
|
|
50
|
+
function assertValidFeCode(feCode) {
|
|
51
|
+
if (!exports.HEX_64.test(feCode)) {
|
|
52
|
+
throw new Error('feCode must be a 64-character hex string');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// Crypto utilities
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
/**
|
|
59
|
+
* Generates a cryptographically random device binding code.
|
|
60
|
+
* @returns 64-character lowercase hex string (32 random bytes).
|
|
61
|
+
*/
|
|
62
|
+
function generateFeCode() {
|
|
63
|
+
return (0, crypto_1.randomBytes)(32).toString('hex');
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Computes the SHA-256 hash of a device binding code.
|
|
67
|
+
* @param feCode - The 64-character hex binding code (case-insensitive input, normalised to lowercase).
|
|
68
|
+
* @returns 64-character lowercase hex SHA-256 hash.
|
|
69
|
+
*/
|
|
70
|
+
function computeFeHash(feCode) {
|
|
71
|
+
assertValidFeCode(feCode);
|
|
72
|
+
// Normalise to lowercase so the hash is deterministic regardless of input case.
|
|
73
|
+
return (0, crypto_1.createHash)('sha256').update(feCode.toLowerCase()).digest('hex');
|
|
74
|
+
}
|
|
75
|
+
/** Characters that must not appear in cookie attribute values (prevents header/cookie injection). */
|
|
76
|
+
const UNSAFE_COOKIE_ATTR = /[;\r\n]/;
|
|
77
|
+
function validateCookieAttr(name, value) {
|
|
78
|
+
if (value === undefined)
|
|
79
|
+
return;
|
|
80
|
+
if (UNSAFE_COOKIE_ATTR.test(value)) {
|
|
81
|
+
throw new Error(`Invalid cookie ${name}: must not contain ';', '\\r', or '\\n'`);
|
|
82
|
+
}
|
|
83
|
+
if (name === 'path' && (value.length === 0 || !value.startsWith('/'))) {
|
|
84
|
+
throw new Error(`Invalid cookie path: must start with '/'`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Builds the Set-Cookie header value to store the device binding code.
|
|
89
|
+
*
|
|
90
|
+
* @param feCode - The binding code to store (64-char hex, normalised to lowercase).
|
|
91
|
+
* @param sessionKey - The session key — used in the cookie name to avoid collisions between parallel sessions.
|
|
92
|
+
* @param options - Optional domain/path overrides.
|
|
93
|
+
* @returns A complete Set-Cookie header value string.
|
|
94
|
+
*/
|
|
95
|
+
function buildSetBindingCookieHeader(feCode, sessionKey, options) {
|
|
96
|
+
assertValidFeCode(feCode);
|
|
97
|
+
validateCookieAttr('domain', options?.domain);
|
|
98
|
+
validateCookieAttr('path', options?.path);
|
|
99
|
+
const secure = options?.secure ?? true;
|
|
100
|
+
const cookieName = getBindingCookieName(sessionKey);
|
|
101
|
+
const parts = [
|
|
102
|
+
`${cookieName}=${feCode.toLowerCase()}`,
|
|
103
|
+
'HttpOnly',
|
|
104
|
+
'SameSite=Lax',
|
|
105
|
+
`Path=${options?.path ?? '/'}`,
|
|
106
|
+
`Max-Age=${exports.BINDING_COOKIE_MAX_AGE}`,
|
|
107
|
+
];
|
|
108
|
+
if (secure) {
|
|
109
|
+
parts.push('Secure');
|
|
110
|
+
}
|
|
111
|
+
if (options?.domain) {
|
|
112
|
+
parts.push(`Domain=${options.domain}`);
|
|
113
|
+
}
|
|
114
|
+
return parts.join('; ');
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Builds the Set-Cookie header value to clear the device binding cookie.
|
|
118
|
+
*
|
|
119
|
+
* @param sessionKey - The session key — identifies which session's cookie to clear.
|
|
120
|
+
* @param options - Optional domain/path (must match the original cookie).
|
|
121
|
+
* @returns A complete Set-Cookie header value string that expires the cookie.
|
|
122
|
+
*/
|
|
123
|
+
function buildClearBindingCookieHeader(sessionKey, options) {
|
|
124
|
+
validateCookieAttr('domain', options?.domain);
|
|
125
|
+
validateCookieAttr('path', options?.path);
|
|
126
|
+
const secure = options?.secure ?? true;
|
|
127
|
+
const cookieName = getBindingCookieName(sessionKey);
|
|
128
|
+
const parts = [
|
|
129
|
+
`${cookieName}=`,
|
|
130
|
+
'HttpOnly',
|
|
131
|
+
'SameSite=Lax',
|
|
132
|
+
`Path=${options?.path ?? '/'}`,
|
|
133
|
+
'Max-Age=0',
|
|
134
|
+
'Expires=Thu, 01 Jan 1970 00:00:00 GMT',
|
|
135
|
+
];
|
|
136
|
+
if (secure) {
|
|
137
|
+
parts.push('Secure');
|
|
138
|
+
}
|
|
139
|
+
if (options?.domain) {
|
|
140
|
+
parts.push(`Domain=${options.domain}`);
|
|
141
|
+
}
|
|
142
|
+
return parts.join('; ');
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Parses the device binding code from a raw Cookie header string for a specific session.
|
|
146
|
+
*
|
|
147
|
+
* Returns `undefined` if the cookie is not present or the value is not
|
|
148
|
+
* a valid 64-character hex string. When found, the value is normalised
|
|
149
|
+
* to lowercase for deterministic hashing downstream.
|
|
150
|
+
*
|
|
151
|
+
* @param cookieHeader - The raw `Cookie` header value (e.g., "foo=bar; _glide_bind_abc123=...").
|
|
152
|
+
* @param sessionKey - The session key that identifies the cookie to look for.
|
|
153
|
+
* @returns The fe_code value (lowercase), or undefined if the cookie is not present or invalid.
|
|
154
|
+
*/
|
|
155
|
+
function parseBindingCookie(cookieHeader, sessionKey) {
|
|
156
|
+
if (!cookieHeader)
|
|
157
|
+
return undefined;
|
|
158
|
+
const cookieName = getBindingCookieName(sessionKey);
|
|
159
|
+
const prefix = `${cookieName}=`;
|
|
160
|
+
const match = cookieHeader.split(';')
|
|
161
|
+
.map(c => c.trim())
|
|
162
|
+
.find(c => c.startsWith(prefix));
|
|
163
|
+
if (!match)
|
|
164
|
+
return undefined;
|
|
165
|
+
const value = match.substring(prefix.length);
|
|
166
|
+
return exports.HEX_64.test(value) ? value.toLowerCase() : undefined;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Builds Set-Cookie headers that expire all previous device binding cookies
|
|
170
|
+
* found in the request.
|
|
171
|
+
*
|
|
172
|
+
* Only one Link flow can be active per browser at a time (App Clip is a
|
|
173
|
+
* single native process), so stale cookies from abandoned flows must be
|
|
174
|
+
* cleared to prevent the old redirect from completing with an outdated
|
|
175
|
+
* binding code.
|
|
176
|
+
*
|
|
177
|
+
* @param cookieHeader - The raw `Cookie` request header.
|
|
178
|
+
* @param options - Cookie options (secure, domain, path) — must match the original set call.
|
|
179
|
+
* @returns Array of Set-Cookie header values that expire stale binding cookies.
|
|
180
|
+
*/
|
|
181
|
+
function clearStaleBindingCookies(cookieHeader, options) {
|
|
182
|
+
if (!cookieHeader)
|
|
183
|
+
return [];
|
|
184
|
+
const headers = [];
|
|
185
|
+
for (const pair of cookieHeader.split(';')) {
|
|
186
|
+
const name = pair.trim().split('=')[0];
|
|
187
|
+
if (name.startsWith(exports.BINDING_COOKIE_PREFIX)) {
|
|
188
|
+
const sessionSuffix = name.substring(exports.BINDING_COOKIE_PREFIX.length);
|
|
189
|
+
if (!sessionSuffix)
|
|
190
|
+
continue; // Skip empty suffix (malformed cookie name)
|
|
191
|
+
try {
|
|
192
|
+
headers.push(buildClearBindingCookieHeader(sessionSuffix, options));
|
|
193
|
+
}
|
|
194
|
+
catch (_) {
|
|
195
|
+
// Malformed session suffix (injection chars) — skip silently.
|
|
196
|
+
// Option validation errors (domain/path) also caught here, but since
|
|
197
|
+
// options are caller-provided and identical for every iteration,
|
|
198
|
+
// an invalid option means ALL iterations would throw anyway.
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return headers;
|
|
203
|
+
}
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
// Completion redirect page
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
/**
|
|
208
|
+
* Returns the HTML for the completion redirect page.
|
|
209
|
+
*
|
|
210
|
+
* This page extracts `agg_code` and `session_key` from the URL fragment,
|
|
211
|
+
* POSTs them to the developer's backend (which auto-includes the
|
|
212
|
+
* `_glide_bind_{sessionKey}` HttpOnly cookie), and displays the result.
|
|
213
|
+
*
|
|
214
|
+
* On successful completion, a **localStorage signal** is written
|
|
215
|
+
* (`glide_signal_{session_key}`) so the FE SDK in the original tab can
|
|
216
|
+
* detect completion via `StorageEvent` without polling. The signal is
|
|
217
|
+
* automatically cleaned up after 5 seconds.
|
|
218
|
+
*
|
|
219
|
+
* @param completeEndpoint - The developer backend endpoint to POST to (e.g., "/api/glide/complete").
|
|
220
|
+
* @returns Complete HTML string ready to serve.
|
|
221
|
+
*/
|
|
222
|
+
function getCompletionPageHtml(completeEndpoint) {
|
|
223
|
+
if (!completeEndpoint.startsWith('/') || completeEndpoint.startsWith('//')) {
|
|
224
|
+
throw new Error('completeEndpoint must be a relative path starting with "/" (e.g., "/api/glide/complete"). ' +
|
|
225
|
+
'Cross-origin and protocol-relative endpoints (e.g., "//evil.com/...") are not supported because ' +
|
|
226
|
+
'the page relies on credentials: "include" for cookie-based device binding, which requires same-origin requests.');
|
|
227
|
+
}
|
|
228
|
+
return `<!DOCTYPE html>
|
|
229
|
+
<html lang="en">
|
|
230
|
+
<head>
|
|
231
|
+
<meta charset="utf-8">
|
|
232
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
233
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; connect-src 'self'">
|
|
234
|
+
<title>Completing verification...</title>
|
|
235
|
+
<style>
|
|
236
|
+
body { font-family: system-ui, -apple-system, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; background: #fafafa; color: #333; }
|
|
237
|
+
.card { text-align: center; padding: 2rem; max-width: 400px; }
|
|
238
|
+
.spinner { width: 40px; height: 40px; border: 3px solid #e0e0e0; border-top-color: #333; border-radius: 50%; animation: spin 0.8s linear infinite; margin: 0 auto 1rem; }
|
|
239
|
+
@keyframes spin { to { transform: rotate(360deg); } }
|
|
240
|
+
.hidden { display: none; }
|
|
241
|
+
.error { color: #c62828; }
|
|
242
|
+
.success { color: #2e7d32; }
|
|
243
|
+
.close-hint { margin-top: 0.75rem; font-size: 0.875rem; color: #888; }
|
|
244
|
+
</style>
|
|
245
|
+
</head>
|
|
246
|
+
<body>
|
|
247
|
+
<div class="card">
|
|
248
|
+
<div id="loading" role="status" aria-live="polite">
|
|
249
|
+
<div class="spinner"></div>
|
|
250
|
+
<p>Completing verification…</p>
|
|
251
|
+
</div>
|
|
252
|
+
<div id="success" class="hidden" role="status" aria-live="polite">
|
|
253
|
+
<p class="success">Verification complete.</p>
|
|
254
|
+
<p id="close-hint" class="close-hint hidden">You may now close this tab.</p>
|
|
255
|
+
</div>
|
|
256
|
+
<div id="error" class="hidden" role="alert" aria-live="assertive">
|
|
257
|
+
<p class="error" id="error-message">Verification failed.</p>
|
|
258
|
+
</div>
|
|
259
|
+
</div>
|
|
260
|
+
<script>
|
|
261
|
+
(async function() {
|
|
262
|
+
var fragment = new URLSearchParams(window.location.hash.substring(1));
|
|
263
|
+
var aggCode = fragment.get('agg_code');
|
|
264
|
+
var sessionKey = fragment.get('session_key');
|
|
265
|
+
|
|
266
|
+
window.history.replaceState(null, '', window.location.pathname + window.location.search);
|
|
267
|
+
|
|
268
|
+
if (!aggCode || !sessionKey) {
|
|
269
|
+
show('error', 'Invalid completion link.');
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
try {
|
|
274
|
+
var res = await fetch(${JSON.stringify(completeEndpoint)}, {
|
|
275
|
+
method: 'POST',
|
|
276
|
+
credentials: 'include',
|
|
277
|
+
headers: { 'Content-Type': 'application/json' },
|
|
278
|
+
body: JSON.stringify({ agg_code: aggCode, session_key: sessionKey, user_agent: navigator.userAgent })
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
if (res.ok) {
|
|
282
|
+
var signalKey = 'glide_signal_' + sessionKey;
|
|
283
|
+
try {
|
|
284
|
+
localStorage.setItem(signalKey, sessionKey);
|
|
285
|
+
setTimeout(function() {
|
|
286
|
+
try { localStorage.removeItem(signalKey); } catch(e) {}
|
|
287
|
+
}, 5000);
|
|
288
|
+
} catch(e) {}
|
|
289
|
+
|
|
290
|
+
show('success');
|
|
291
|
+
setTimeout(function() { tryClose(); }, 1500);
|
|
292
|
+
} else {
|
|
293
|
+
var errMsg = 'Verification failed. Please try again.';
|
|
294
|
+
try {
|
|
295
|
+
var data = await res.json();
|
|
296
|
+
if (data && data.message) errMsg = data.message;
|
|
297
|
+
} catch (_) {}
|
|
298
|
+
show('error', errMsg);
|
|
299
|
+
}
|
|
300
|
+
} catch (e) {
|
|
301
|
+
show('error', 'Network error. Please try again.');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function tryClose() {
|
|
305
|
+
window.close();
|
|
306
|
+
try { window.open('', '_self').close(); } catch(e) {}
|
|
307
|
+
setTimeout(function() {
|
|
308
|
+
document.getElementById('close-hint').classList.remove('hidden');
|
|
309
|
+
}, 200);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function show(id, msg) {
|
|
313
|
+
document.getElementById('loading').classList.add('hidden');
|
|
314
|
+
document.getElementById(id).classList.remove('hidden');
|
|
315
|
+
if (msg) document.getElementById('error-message').textContent = msg;
|
|
316
|
+
}
|
|
317
|
+
})();
|
|
318
|
+
</script>
|
|
319
|
+
</body>
|
|
320
|
+
</html>`;
|
|
321
|
+
}
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uCAAuC,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAC;AAE3E,OAAO,EAAE,SAAS,EAAE,CAAC;AACrB,YAAY,EAAE,aAAa,EAAE,CAAC;AAE9B;;;;;;;;;;;GAWG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACvC,SAAgB,IAAI,sBAAsB;IAC1C,+CAA+C;IAC/C,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,wBAAwB;IACxB,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,gCAAgC;IAChC,SAAgB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnC,SAAgB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnC,SAAgB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjC,SAAgB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChC,SAAgB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjC,SAAgB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAEtC,aAAa,EAAE;QACvB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACrC;IAsBD,kDAAkD;IAClD,EAAE,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO;IAIhC,MAAM;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uCAAuC,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAC;AAE3E,OAAO,EAAE,SAAS,EAAE,CAAC;AACrB,YAAY,EAAE,aAAa,EAAE,CAAC;AAE9B;;;;;;;;;;;GAWG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACvC,SAAgB,IAAI,sBAAsB;IAC1C,+CAA+C;IAC/C,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,wBAAwB;IACxB,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,gCAAgC;IAChC,SAAgB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnC,SAAgB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnC,SAAgB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjC,SAAgB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChC,SAAgB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjC,SAAgB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAEtC,aAAa,EAAE;QACvB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACrC;IAsBD,kDAAkD;IAClD,EAAE,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO;IAIhC,MAAM;;;;;;;;;;;CAaT"}
|
package/dist/errors.js
CHANGED
|
@@ -52,14 +52,5 @@ class MagicalAuthError extends Error {
|
|
|
52
52
|
details: this.details
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
|
-
/** Custom inspect for cleaner console output (Node.js). */
|
|
56
|
-
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
57
|
-
const props = [`code: '${this.code}'`, `status: ${this.status}`];
|
|
58
|
-
if (this.requestId)
|
|
59
|
-
props.push(`requestId: '${this.requestId}'`);
|
|
60
|
-
if (this.details)
|
|
61
|
-
props.push(`details: ${JSON.stringify(this.details)}`);
|
|
62
|
-
return `MagicalAuthError: ${this.message} { ${props.join(', ')} }`;
|
|
63
|
-
}
|
|
64
55
|
}
|
|
65
56
|
exports.MagicalAuthError = MagicalAuthError;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
export * from '@glideidentity/glide-be-sdk-node-core';
|
|
2
|
-
export type { GlideSdkSettings, LogFormat, TokenResponse, CachedToken } from './types';
|
|
2
|
+
export type { GlideSdkSettings, LogFormat, TokenResponse, CachedToken, PrepareResult } from './types';
|
|
3
3
|
export { GlideClient } from './glide';
|
|
4
4
|
export type { Logger, LogField } from './logger';
|
|
5
5
|
export { LogLevel, createLogger, generateRequestId } from './logger';
|
|
6
6
|
export { MagicalAuthError } from './errors';
|
|
7
7
|
export { validatePhoneNumber, validatePlmn, validateUseCaseRequirements } from './validation';
|
|
8
8
|
export type { ValidationResult } from './validation';
|
|
9
|
+
export { BINDING_COOKIE_PREFIX, BINDING_COOKIE_MAX_AGE, getBindingCookieName, HEX_64, assertValidFeCode, generateFeCode, computeFeHash, buildSetBindingCookieHeader, buildClearBindingCookieHeader, parseBindingCookie, clearStaleBindingCookies, getCompletionPageHtml, } from './device-binding';
|
|
10
|
+
export type { BindingCookieOptions } from './device-binding';
|
|
9
11
|
export { request, FetchError } from './internal/http';
|
|
10
12
|
export type { RequestOptions, HttpResponse } from './internal/http';
|
|
11
13
|
export { getStatusUrl } from './utils';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,uCAAuC,CAAC;AAGtD,YAAY,EAAE,gBAAgB,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,uCAAuC,CAAC;AAGtD,YAAY,EAAE,gBAAgB,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGtG,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAGtC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAGrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAG5C,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAC9F,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrD,OAAO,EACH,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,EACpB,MAAM,EACN,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,2BAA2B,EAC3B,6BAA6B,EAC7B,kBAAkB,EAClB,wBAAwB,EACxB,qBAAqB,GACxB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAG7D,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACtD,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGpE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.getStatusUrl = exports.FetchError = exports.request = exports.validateUseCaseRequirements = exports.validatePlmn = exports.validatePhoneNumber = exports.MagicalAuthError = exports.generateRequestId = exports.createLogger = exports.LogLevel = exports.GlideClient = void 0;
|
|
17
|
+
exports.getStatusUrl = exports.FetchError = exports.request = exports.getCompletionPageHtml = exports.clearStaleBindingCookies = exports.parseBindingCookie = exports.buildClearBindingCookieHeader = exports.buildSetBindingCookieHeader = exports.computeFeHash = exports.generateFeCode = exports.assertValidFeCode = exports.HEX_64 = exports.getBindingCookieName = exports.BINDING_COOKIE_MAX_AGE = exports.BINDING_COOKIE_PREFIX = exports.validateUseCaseRequirements = exports.validatePlmn = exports.validatePhoneNumber = exports.MagicalAuthError = exports.generateRequestId = exports.createLogger = exports.LogLevel = exports.GlideClient = void 0;
|
|
18
18
|
// Re-export all core types and constants (single source of truth)
|
|
19
19
|
__exportStar(require("@glideidentity/glide-be-sdk-node-core"), exports);
|
|
20
20
|
// Main client
|
|
@@ -32,6 +32,20 @@ var validation_1 = require("./validation");
|
|
|
32
32
|
Object.defineProperty(exports, "validatePhoneNumber", { enumerable: true, get: function () { return validation_1.validatePhoneNumber; } });
|
|
33
33
|
Object.defineProperty(exports, "validatePlmn", { enumerable: true, get: function () { return validation_1.validatePlmn; } });
|
|
34
34
|
Object.defineProperty(exports, "validateUseCaseRequirements", { enumerable: true, get: function () { return validation_1.validateUseCaseRequirements; } });
|
|
35
|
+
// Device binding utilities
|
|
36
|
+
var device_binding_1 = require("./device-binding");
|
|
37
|
+
Object.defineProperty(exports, "BINDING_COOKIE_PREFIX", { enumerable: true, get: function () { return device_binding_1.BINDING_COOKIE_PREFIX; } });
|
|
38
|
+
Object.defineProperty(exports, "BINDING_COOKIE_MAX_AGE", { enumerable: true, get: function () { return device_binding_1.BINDING_COOKIE_MAX_AGE; } });
|
|
39
|
+
Object.defineProperty(exports, "getBindingCookieName", { enumerable: true, get: function () { return device_binding_1.getBindingCookieName; } });
|
|
40
|
+
Object.defineProperty(exports, "HEX_64", { enumerable: true, get: function () { return device_binding_1.HEX_64; } });
|
|
41
|
+
Object.defineProperty(exports, "assertValidFeCode", { enumerable: true, get: function () { return device_binding_1.assertValidFeCode; } });
|
|
42
|
+
Object.defineProperty(exports, "generateFeCode", { enumerable: true, get: function () { return device_binding_1.generateFeCode; } });
|
|
43
|
+
Object.defineProperty(exports, "computeFeHash", { enumerable: true, get: function () { return device_binding_1.computeFeHash; } });
|
|
44
|
+
Object.defineProperty(exports, "buildSetBindingCookieHeader", { enumerable: true, get: function () { return device_binding_1.buildSetBindingCookieHeader; } });
|
|
45
|
+
Object.defineProperty(exports, "buildClearBindingCookieHeader", { enumerable: true, get: function () { return device_binding_1.buildClearBindingCookieHeader; } });
|
|
46
|
+
Object.defineProperty(exports, "parseBindingCookie", { enumerable: true, get: function () { return device_binding_1.parseBindingCookie; } });
|
|
47
|
+
Object.defineProperty(exports, "clearStaleBindingCookies", { enumerable: true, get: function () { return device_binding_1.clearStaleBindingCookies; } });
|
|
48
|
+
Object.defineProperty(exports, "getCompletionPageHtml", { enumerable: true, get: function () { return device_binding_1.getCompletionPageHtml; } });
|
|
35
49
|
// HTTP utilities - for advanced usage and dev-server
|
|
36
50
|
var http_1 = require("./internal/http");
|
|
37
51
|
Object.defineProperty(exports, "request", { enumerable: true, get: function () { return http_1.request; } });
|
package/dist/logger.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,2BAA2B;AAC3B,oBAAY,QAAQ;IAChB,KAAK,IAAI;IACT,IAAI,IAAI;IACR,IAAI,IAAI;IACR,KAAK,IAAI;CACZ;AAED,4BAA4B;AAC5B,MAAM,WAAW,QAAQ;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAED,mDAAmD;AACnD,MAAM,WAAW,MAAM;IACnB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAChD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC/C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC/C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;CACnD;
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,2BAA2B;AAC3B,oBAAY,QAAQ;IAChB,KAAK,IAAI;IACT,IAAI,IAAI;IACR,IAAI,IAAI;IACR,KAAK,IAAI;CACZ;AAED,4BAA4B;AAC5B,MAAM,WAAW,QAAQ;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAED,mDAAmD;AACnD,MAAM,WAAW,MAAM;IACnB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAChD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC/C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC/C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;CACnD;AAmHD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,SAAS,GAAG,MAAM,CAG/E;AAED,qCAAqC;AACrC,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C"}
|
package/dist/logger.js
CHANGED
|
@@ -13,7 +13,9 @@ var LogLevel;
|
|
|
13
13
|
})(LogLevel || (exports.LogLevel = LogLevel = {}));
|
|
14
14
|
const SENSITIVE_KEYS = [
|
|
15
15
|
'apikey', 'api_key', 'api-key', 'token', 'password', 'secret',
|
|
16
|
-
'credential', 'authorization', 'bearer', 'key'
|
|
16
|
+
'credential', 'authorization', 'bearer', 'key',
|
|
17
|
+
'fe_code', 'fecode', 'fe_hash', 'fehash', 'agg_code', 'aggcode',
|
|
18
|
+
'_glide_bind', 'glide_bind'
|
|
17
19
|
];
|
|
18
20
|
const SENSITIVE_PATTERN = new RegExp(`(${SENSITIVE_KEYS.join('|')})([=:\\s"']+)([^\\s"'&,}{\\]\\)]+)`, 'gi');
|
|
19
21
|
function sanitizeMessage(message) {
|
|
@@ -1,15 +1,22 @@
|
|
|
1
|
-
import { GlideSdkSettings } from '../types';
|
|
1
|
+
import { GlideSdkSettings, PrepareResult } from '../types';
|
|
2
2
|
import { Logger } from '../logger';
|
|
3
3
|
import { OAuthManager } from '../internal/oauth';
|
|
4
|
-
import type { PrepareRequest,
|
|
4
|
+
import type { PrepareRequest, CompleteRequest, VerifyPhoneNumberRequest, VerifyPhoneNumberResponse, GetPhoneNumberRequest, GetPhoneNumberResponse, ReportInvocationRequest, ReportInvocationResponse, StatusResponse } from '@glideidentity/glide-be-sdk-node-core';
|
|
5
5
|
/** Handles SIM-based phone authentication via the Glide API. */
|
|
6
6
|
export declare class MagicalAuth {
|
|
7
7
|
private settings;
|
|
8
8
|
private oauthClient;
|
|
9
9
|
private logger;
|
|
10
10
|
constructor(settings: GlideSdkSettings, oauthClient: OAuthManager, logger: Logger);
|
|
11
|
-
/**
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Initiates a phone authentication session.
|
|
13
|
+
*
|
|
14
|
+
* For link protocol sessions, this method automatically generates a device
|
|
15
|
+
* binding code (`feCode`) and includes its hash in the aggregator request.
|
|
16
|
+
* When `feCode` is present in the result, the caller MUST set it as an
|
|
17
|
+
* HttpOnly cookie — use `buildSetBindingCookieHeader()` from `device-binding`.
|
|
18
|
+
*/
|
|
19
|
+
prepare(req: PrepareRequest): Promise<PrepareResult>;
|
|
13
20
|
/** Verifies a phone number matches the device. */
|
|
14
21
|
verifyPhoneNumber(req: VerifyPhoneNumberRequest): Promise<VerifyPhoneNumberResponse>;
|
|
15
22
|
/** Retrieves the phone number. */
|
|
@@ -27,6 +34,28 @@ export declare class MagicalAuth {
|
|
|
27
34
|
* @returns Response with success status
|
|
28
35
|
*/
|
|
29
36
|
reportInvocation(req: ReportInvocationRequest): Promise<ReportInvocationResponse>;
|
|
37
|
+
/**
|
|
38
|
+
* Completes a device-bound authentication session.
|
|
39
|
+
*
|
|
40
|
+
* Call this after the completion redirect page POSTs `agg_code` and `session_key`
|
|
41
|
+
* to the developer backend. The `fe_code` should be read from the `_glide_bind`
|
|
42
|
+
* HttpOnly cookie — use `parseBindingCookie()` from `device-binding`.
|
|
43
|
+
*
|
|
44
|
+
* The complete endpoint does not return verification results. The client-side
|
|
45
|
+
* poller detects the session status change to `completed` and calls `/process`.
|
|
46
|
+
*
|
|
47
|
+
* @param req - The complete request containing session_key, fe_code, and agg_code.
|
|
48
|
+
*/
|
|
49
|
+
complete(req: CompleteRequest): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Checks the status of an authentication session.
|
|
52
|
+
*
|
|
53
|
+
* Use this to poll for completion of async flows (link/desktop).
|
|
54
|
+
* This is the authenticated version — requires a Bearer token.
|
|
55
|
+
*
|
|
56
|
+
* @param sessionKey - The 16-character hex session key from the prepare response.
|
|
57
|
+
*/
|
|
58
|
+
checkStatus(sessionKey: string): Promise<StatusResponse>;
|
|
30
59
|
private handleError;
|
|
31
60
|
}
|
|
32
61
|
//# sourceMappingURL=magical-auth.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"magical-auth.d.ts","sourceRoot":"","sources":["../../src/services/magical-auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"magical-auth.d.ts","sourceRoot":"","sources":["../../src/services/magical-auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnC,OAAO,EAAE,YAAY,EAAc,MAAM,mBAAmB,CAAC;AAK7D,OAAO,KAAK,EACR,cAAc,EAEd,eAAe,EACf,wBAAwB,EACxB,yBAAyB,EACzB,qBAAqB,EACrB,sBAAsB,EACtB,uBAAuB,EACvB,wBAAwB,EACxB,cAAc,EACjB,MAAM,uCAAuC,CAAC;AAa/C,gEAAgE;AAChE,qBAAa,WAAW;IACpB,OAAO,CAAC,QAAQ,CAAmB;IACnC,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,MAAM,CAAS;gBAEX,QAAQ,EAAE,gBAAgB,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM;IAMjF;;;;;;;OAOG;IACG,OAAO,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAkG1D,kDAAkD;IAC5C,iBAAiB,CAAC,GAAG,EAAE,wBAAwB,GAAG,OAAO,CAAC,yBAAyB,CAAC;IA8C1F,kCAAkC;IAC5B,cAAc,CAAC,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IA8CjF;;;;;;;;;;;OAWG;IACG,gBAAgB,CAAC,GAAG,EAAE,uBAAuB,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAsCvF;;;;;;;;;;;OAWG;IACG,QAAQ,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAiEnD;;;;;;;OAOG;IACG,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAgC9D,OAAO,CAAC,WAAW;CAyCtB"}
|
|
@@ -5,6 +5,8 @@ const http_1 = require("../internal/http");
|
|
|
5
5
|
const oauth_1 = require("../internal/oauth");
|
|
6
6
|
const errors_1 = require("../errors");
|
|
7
7
|
const validation_1 = require("../validation");
|
|
8
|
+
const device_binding_1 = require("../device-binding");
|
|
9
|
+
const glide_be_sdk_node_core_1 = require("@glideidentity/glide-be-sdk-node-core");
|
|
8
10
|
function generateNonce(length = 32) {
|
|
9
11
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
10
12
|
let result = '';
|
|
@@ -22,7 +24,14 @@ class MagicalAuth {
|
|
|
22
24
|
this.oauthClient = oauthClient;
|
|
23
25
|
this.logger = logger;
|
|
24
26
|
}
|
|
25
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Initiates a phone authentication session.
|
|
29
|
+
*
|
|
30
|
+
* For link protocol sessions, this method automatically generates a device
|
|
31
|
+
* binding code (`feCode`) and includes its hash in the aggregator request.
|
|
32
|
+
* When `feCode` is present in the result, the caller MUST set it as an
|
|
33
|
+
* HttpOnly cookie — use `buildSetBindingCookieHeader()` from `device-binding`.
|
|
34
|
+
*/
|
|
26
35
|
async prepare(req) {
|
|
27
36
|
const hasParentSession = req.options?.parent_session_id;
|
|
28
37
|
if (!hasParentSession) {
|
|
@@ -31,8 +40,7 @@ class MagicalAuth {
|
|
|
31
40
|
throw new errors_1.MagicalAuthError({
|
|
32
41
|
code: useCaseValidation.errorCode || errors_1.ErrorCode.INVALID_USE_CASE,
|
|
33
42
|
message: useCaseValidation.error,
|
|
34
|
-
status: 400
|
|
35
|
-
details: useCaseValidation.details
|
|
43
|
+
status: 400
|
|
36
44
|
});
|
|
37
45
|
}
|
|
38
46
|
}
|
|
@@ -56,24 +64,29 @@ class MagicalAuth {
|
|
|
56
64
|
});
|
|
57
65
|
}
|
|
58
66
|
}
|
|
67
|
+
// Generate device binding code and hash for potential link protocol sessions.
|
|
68
|
+
// Always sent — the aggregator ignores it for non-link strategies (TS43, desktop).
|
|
69
|
+
const feCode = (0, device_binding_1.generateFeCode)();
|
|
70
|
+
const feHash = (0, device_binding_1.computeFeHash)(feCode);
|
|
59
71
|
const nonce = req.nonce || generateNonce();
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
// Only include optional fields when truthy to avoid sending null / ""
|
|
73
|
+
// to the aggregator — JSON.stringify serializes null and "" but the
|
|
74
|
+
// aggregator may reject or misinterpret them.
|
|
75
|
+
const body = {
|
|
76
|
+
nonce,
|
|
77
|
+
fe_hash: feHash,
|
|
78
|
+
...(req.use_case ? { use_case: req.use_case } : undefined),
|
|
79
|
+
...(req.phone_number ? { phone_number: req.phone_number } : undefined),
|
|
80
|
+
...(req.plmn ? { plmn: req.plmn } : undefined),
|
|
81
|
+
...(req.consent_data ? { consent_data: req.consent_data } : undefined),
|
|
82
|
+
...(req.options ? { options: req.options } : undefined),
|
|
83
|
+
};
|
|
84
|
+
// Always include client_info so the aggregator can compare the
|
|
85
|
+
// browser user-agent between /prepare and /complete (browser mismatch detection).
|
|
86
|
+
body.client_info = {
|
|
87
|
+
user_agent: req.client_info?.user_agent || 'unknown',
|
|
88
|
+
platform: req.client_info?.platform || 'unknown'
|
|
89
|
+
};
|
|
77
90
|
const url = `${this.settings.baseUrl}/magic-auth/v2/auth/prepare`;
|
|
78
91
|
this.logger.debug('Prepare request', { url });
|
|
79
92
|
try {
|
|
@@ -89,7 +102,12 @@ class MagicalAuth {
|
|
|
89
102
|
});
|
|
90
103
|
const result = response.json();
|
|
91
104
|
this.logger.debug('Prepare completed', { strategy: result.authentication_strategy });
|
|
92
|
-
|
|
105
|
+
// Only expose feCode when the session uses link protocol (device binding).
|
|
106
|
+
const prepareResult = { ...result };
|
|
107
|
+
if (result.authentication_strategy === glide_be_sdk_node_core_1.AuthenticationStrategy.LINK) {
|
|
108
|
+
prepareResult.feCode = feCode;
|
|
109
|
+
}
|
|
110
|
+
return prepareResult;
|
|
93
111
|
}
|
|
94
112
|
catch (error) {
|
|
95
113
|
this.handleError(error);
|
|
@@ -214,7 +232,112 @@ class MagicalAuth {
|
|
|
214
232
|
body: JSON.stringify(body),
|
|
215
233
|
});
|
|
216
234
|
const result = await response.json();
|
|
217
|
-
this.logger.debug('ReportInvocation completed', { session_id: req.session_id,
|
|
235
|
+
this.logger.debug('ReportInvocation completed', { session_id: req.session_id, status: result.status });
|
|
236
|
+
return result;
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
239
|
+
this.handleError(error);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Completes a device-bound authentication session.
|
|
244
|
+
*
|
|
245
|
+
* Call this after the completion redirect page POSTs `agg_code` and `session_key`
|
|
246
|
+
* to the developer backend. The `fe_code` should be read from the `_glide_bind`
|
|
247
|
+
* HttpOnly cookie — use `parseBindingCookie()` from `device-binding`.
|
|
248
|
+
*
|
|
249
|
+
* The complete endpoint does not return verification results. The client-side
|
|
250
|
+
* poller detects the session status change to `completed` and calls `/process`.
|
|
251
|
+
*
|
|
252
|
+
* @param req - The complete request containing session_key, fe_code, and agg_code.
|
|
253
|
+
*/
|
|
254
|
+
async complete(req) {
|
|
255
|
+
if (!req.session_key) {
|
|
256
|
+
throw new errors_1.MagicalAuthError({
|
|
257
|
+
code: errors_1.ErrorCode.MISSING_REQUIRED_FIELD,
|
|
258
|
+
message: 'session_key is required',
|
|
259
|
+
status: 400
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
if (!req.fe_code) {
|
|
263
|
+
throw new errors_1.MagicalAuthError({
|
|
264
|
+
code: errors_1.ErrorCode.MISSING_BINDING_COOKIE,
|
|
265
|
+
message: 'fe_code is required (from _glide_bind cookie)',
|
|
266
|
+
status: 403
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
if (!req.agg_code) {
|
|
270
|
+
throw new errors_1.MagicalAuthError({
|
|
271
|
+
code: errors_1.ErrorCode.MISSING_REQUIRED_FIELD,
|
|
272
|
+
message: 'agg_code is required',
|
|
273
|
+
status: 400
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
// Validate format — both codes must be 64-char lowercase hex.
|
|
277
|
+
// Use a generic 403 to avoid leaking which value failed.
|
|
278
|
+
if (!device_binding_1.HEX_64.test(req.fe_code) || !device_binding_1.HEX_64.test(req.agg_code)) {
|
|
279
|
+
throw new errors_1.MagicalAuthError({
|
|
280
|
+
code: errors_1.ErrorCode.DEVICE_BINDING_FAILED,
|
|
281
|
+
message: 'Invalid device binding inputs',
|
|
282
|
+
status: 403
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
const body = {
|
|
286
|
+
session_key: req.session_key,
|
|
287
|
+
fe_code: req.fe_code,
|
|
288
|
+
agg_code: req.agg_code,
|
|
289
|
+
};
|
|
290
|
+
if (req.user_agent) {
|
|
291
|
+
body.user_agent = req.user_agent;
|
|
292
|
+
}
|
|
293
|
+
const url = `${this.settings.baseUrl}/magic-auth/v2/auth/complete`;
|
|
294
|
+
this.logger.debug('Complete request', { url });
|
|
295
|
+
try {
|
|
296
|
+
const accessToken = await this.oauthClient.getAccessToken();
|
|
297
|
+
await (0, http_1.request)(url, {
|
|
298
|
+
method: 'POST',
|
|
299
|
+
headers: {
|
|
300
|
+
'Content-Type': 'application/json',
|
|
301
|
+
'Accept': 'application/json',
|
|
302
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
303
|
+
},
|
|
304
|
+
body: JSON.stringify(body),
|
|
305
|
+
});
|
|
306
|
+
this.logger.debug('Complete succeeded');
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
this.handleError(error);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Checks the status of an authentication session.
|
|
314
|
+
*
|
|
315
|
+
* Use this to poll for completion of async flows (link/desktop).
|
|
316
|
+
* This is the authenticated version — requires a Bearer token.
|
|
317
|
+
*
|
|
318
|
+
* @param sessionKey - The 16-character hex session key from the prepare response.
|
|
319
|
+
*/
|
|
320
|
+
async checkStatus(sessionKey) {
|
|
321
|
+
if (!sessionKey) {
|
|
322
|
+
throw new errors_1.MagicalAuthError({
|
|
323
|
+
code: errors_1.ErrorCode.MISSING_REQUIRED_FIELD,
|
|
324
|
+
message: 'sessionKey is required',
|
|
325
|
+
status: 400
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
const url = `${this.settings.baseUrl}/magic-auth/v2/auth/status/${sessionKey}`;
|
|
329
|
+
this.logger.debug('CheckStatus request', { url, sessionKey });
|
|
330
|
+
try {
|
|
331
|
+
const accessToken = await this.oauthClient.getAccessToken();
|
|
332
|
+
const response = await (0, http_1.request)(url, {
|
|
333
|
+
method: 'GET',
|
|
334
|
+
headers: {
|
|
335
|
+
'Accept': 'application/json',
|
|
336
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
337
|
+
},
|
|
338
|
+
});
|
|
339
|
+
const result = response.json();
|
|
340
|
+
this.logger.debug('CheckStatus completed', { sessionKey, status: result.status });
|
|
218
341
|
return result;
|
|
219
342
|
}
|
|
220
343
|
catch (error) {
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Logger, LogLevel } from './logger';
|
|
2
|
+
import type { PrepareResponse } from '@glideidentity/glide-be-sdk-node-core';
|
|
2
3
|
/** Log output format. */
|
|
3
4
|
export type LogFormat = 'json' | 'text';
|
|
4
5
|
/** OAuth2 token response from the token endpoint. */
|
|
@@ -19,6 +20,24 @@ export interface CachedToken {
|
|
|
19
20
|
/** Timestamp (ms) when the token expires. */
|
|
20
21
|
expiresAt: number;
|
|
21
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Extended prepare response that includes the device binding code for link protocol.
|
|
25
|
+
*
|
|
26
|
+
* Compatible with `PrepareResponse` — existing code that expects `PrepareResponse` will
|
|
27
|
+
* continue to work unchanged. The `feCode` field is only present when the session uses
|
|
28
|
+
* the link authentication strategy.
|
|
29
|
+
*/
|
|
30
|
+
export interface PrepareResult extends PrepareResponse {
|
|
31
|
+
/**
|
|
32
|
+
* Device binding code for link protocol sessions.
|
|
33
|
+
*
|
|
34
|
+
* When present, the caller MUST set this as an HttpOnly cookie using
|
|
35
|
+
* `buildSetBindingCookieHeader(feCode)` from `device-binding.ts`.
|
|
36
|
+
*
|
|
37
|
+
* `undefined` for non-link strategies (TS43, desktop).
|
|
38
|
+
*/
|
|
39
|
+
feCode?: string;
|
|
40
|
+
}
|
|
22
41
|
/** Client configuration options. */
|
|
23
42
|
export interface GlideSdkSettings {
|
|
24
43
|
/** OAuth2 Client ID (required). */
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAE7E,yBAAyB;AACzB,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAExC,qDAAqD;AACrD,MAAM,WAAW,aAAa;IAC1B,6CAA6C;IAC7C,YAAY,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,uBAAuB;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,6CAA6C;AAC7C,MAAM,WAAW,WAAW;IACxB,wBAAwB;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAc,SAAQ,eAAe;IAClD;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,oCAAoC;AACpC,MAAM,WAAW,gBAAgB;IAC7B,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,iBAAiB;IACjB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC/B"}
|
package/dist/validation.d.ts
CHANGED
|
@@ -4,7 +4,6 @@ export interface ValidationResult {
|
|
|
4
4
|
valid: boolean;
|
|
5
5
|
error?: string;
|
|
6
6
|
errorCode?: string;
|
|
7
|
-
details?: Record<string, unknown>;
|
|
8
7
|
}
|
|
9
8
|
/** Validates phone number format (E.164). */
|
|
10
9
|
export declare function validatePhoneNumber(phoneNumber?: string): ValidationResult;
|
package/dist/validation.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AAE/E,yBAAyB;AACzB,MAAM,WAAW,gBAAgB;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AAE/E,yBAAyB;AACzB,MAAM,WAAW,gBAAgB;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,6CAA6C;AAC7C,wBAAgB,mBAAmB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAyC1E;AAED,uCAAuC;AACvC,wBAAgB,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,GAAG,gBAAgB,CAoB1D;AAED,+EAA+E;AAC/E,wBAAgB,2BAA2B,CACvC,OAAO,CAAC,EAAE,WAAW,EACrB,WAAW,CAAC,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,IAAI,GACZ,gBAAgB,CAuClB"}
|
package/dist/validation.js
CHANGED
|
@@ -75,16 +75,14 @@ function validateUseCaseRequirements(useCase, phoneNumber, plmn) {
|
|
|
75
75
|
return {
|
|
76
76
|
valid: false,
|
|
77
77
|
error: 'Phone number should not be provided for GetPhoneNumber use case',
|
|
78
|
-
errorCode: glide_be_sdk_node_core_1.ErrorCode.INVALID_USE_CASE
|
|
79
|
-
details: { invalid_field: 'phone_number', reason: 'not_allowed_for_use_case' }
|
|
78
|
+
errorCode: glide_be_sdk_node_core_1.ErrorCode.INVALID_USE_CASE
|
|
80
79
|
};
|
|
81
80
|
}
|
|
82
81
|
if (!plmn) {
|
|
83
82
|
return {
|
|
84
83
|
valid: false,
|
|
85
84
|
error: 'PLMN (MCC/MNC) is required for GetPhoneNumber use case',
|
|
86
|
-
errorCode: glide_be_sdk_node_core_1.ErrorCode.MISSING_REQUIRED_FIELD
|
|
87
|
-
details: { missing_field: 'plmn' }
|
|
85
|
+
errorCode: glide_be_sdk_node_core_1.ErrorCode.MISSING_REQUIRED_FIELD
|
|
88
86
|
};
|
|
89
87
|
}
|
|
90
88
|
break;
|
|
@@ -93,8 +91,7 @@ function validateUseCaseRequirements(useCase, phoneNumber, plmn) {
|
|
|
93
91
|
return {
|
|
94
92
|
valid: false,
|
|
95
93
|
error: 'Phone number is required for VerifyPhoneNumber use case',
|
|
96
|
-
errorCode: glide_be_sdk_node_core_1.ErrorCode.MISSING_REQUIRED_FIELD
|
|
97
|
-
details: { missing_field: 'phone_number' }
|
|
94
|
+
errorCode: glide_be_sdk_node_core_1.ErrorCode.MISSING_REQUIRED_FIELD
|
|
98
95
|
};
|
|
99
96
|
}
|
|
100
97
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glideidentity/glide-be-sdk-node",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Glide SDK for Node.js - carrier-based phone verification",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -21,8 +21,17 @@
|
|
|
21
21
|
"clean": "rm -rf dist"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@glideidentity/glide-be-sdk-node-core": "2.0
|
|
24
|
+
"@glideidentity/glide-be-sdk-node-core": "2.1.0"
|
|
25
25
|
},
|
|
26
|
-
"
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"registry": "https://us-central1-npm.pkg.dev/platform-devops-09ff133a/npm-packages/"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"glide",
|
|
31
|
+
"sdk",
|
|
32
|
+
"phone",
|
|
33
|
+
"verification",
|
|
34
|
+
"magic-auth"
|
|
35
|
+
],
|
|
27
36
|
"license": "SEE LICENSE IN LICENSE"
|
|
28
37
|
}
|