@descope/node-sdk 1.0.4-alpha.5 → 1.0.4-alpha.6
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 +11 -216
- package/dist/cjs/index.cjs.js +1 -1
- package/dist/cjs/index.cjs.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.umd.js +1 -16
- package/dist/index.umd.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,228 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
Use the Descope NodeJS SDK for NodeJS/Express to quickly and easily add user authentication to your application or website.
|
|
4
4
|
|
|
5
|
+
## Installing the SDK
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
Replace any instance of `<ProjectID>` in the code below with your company's Project ID, which can be found in the [Descope console](https://app.descope.com).
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
Run the following code in your project. These commands will add the Descope SDK for Node as a project dependency, and set the `DESCOPE_PROJECT_ID` variable to a valid \<ProjectID\>.
|
|
9
10
|
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
signup[1. customer sign-up]-- customer gets OTP -->verify[3. customer verification]
|
|
13
|
-
signin[2. customer sign-in]-- customer gets OTP -->verify
|
|
14
|
-
verify-- access private API -->validate[4. session validation]
|
|
11
|
+
```bash
|
|
12
|
+
npm i --save @descope/node-sdk
|
|
15
13
|
```
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
Replace any instance of `<ProjectID>` in the code below with your company's Project ID, which can be found in the [Descope console](https://app.descope.com).
|
|
20
|
-
|
|
21
|
-
* Run the following commands in your project
|
|
22
|
-
|
|
23
|
-
These commands will add the Descope NodeJS SDK as a project dependency.
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
npm i --save @descope/node-sdk
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
* Import and initialize the ExpresSDK for NodeJS client in your source code
|
|
30
|
-
|
|
31
|
-
```javascript
|
|
32
|
-
import DescopeClient from '@descope/node-sdk';
|
|
33
|
-
const descopeClient = DescopeClient({ projectId: <ProjectID> });
|
|
34
|
-
```
|
|
35
|
-
or
|
|
36
|
-
|
|
37
|
-
```javascript
|
|
38
|
-
const sdk = require('@descope/node-sdk');
|
|
39
|
-
const descopeClient = sdk({ projectId: <ProjectID> });
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### 1. Customer Sign-up
|
|
43
|
-
|
|
44
|
-
In your sign-up route for OTP (for example, `myapp.com/signup`) generate a sign-up request and send the OTP verification code via the selected delivery method. In the example below an email is sent to "mytestmail@test.com". In additon, optional user data (for exmaple, a custom username in the code sample below) can be gathered during the sign-up process.
|
|
45
|
-
|
|
46
|
-
```javascript
|
|
47
|
-
await descopeClient.otp.signUp.email("mytestmail@test.com");
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### 2. Customer Sign-in
|
|
51
|
-
In your sign-in route for OTP (for exmaple, `myapp.com/login`) generate a sign-in request send the OTP verification code via the selected delivery method. In the example below an email is sent to "mytestmail@test.com".
|
|
52
|
-
|
|
53
|
-
```javascript
|
|
54
|
-
await descopeClient.otp.signIn.email("mytestmail@test.com");
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
### 3. Customer Verification
|
|
58
|
-
|
|
59
|
-
In your verify customer route for OTP (for example, `myapp.com/verify`) verify the OTP from either a customer sign-up or sign-in. The VerifyCode function call will write the necessary tokens and cookies to the response writer (`w`), which will be used by the NodeJS client to validate each session interaction.
|
|
60
|
-
|
|
61
|
-
```javascript
|
|
62
|
-
const out = await descopeClient.otp.verify.email(identifier, code);
|
|
63
|
-
if (out.data.cookies) {
|
|
64
|
-
res.set('Set-Cookie', out.data.cookies);
|
|
65
|
-
}
|
|
66
|
-
```
|
|
15
|
+
## What do you want to implement?
|
|
67
16
|
|
|
68
|
-
|
|
17
|
+
Click one of the following links to open the documentation for that specific functionality.
|
|
69
18
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const out = await descopeClient.validateSession(session_jwt, refresh_jwt);
|
|
74
|
-
if (out?.cookies) {
|
|
75
|
-
res.set('Set-Cookie', out.cookies);
|
|
76
|
-
}
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
## ExpressStart with MagicLink Authentication
|
|
80
|
-
|
|
81
|
-
This section will help you implement user authentication using Magiclinks. A typical four step flow for OTP authentictaion is shown below.
|
|
82
|
-
|
|
83
|
-
```mermaid
|
|
84
|
-
flowchart LR
|
|
85
|
-
signup[1. customer sign-up]-- customer gets MagicLink -->verify[3. MagicLink verification]
|
|
86
|
-
signin[2. customer sign-in]-- customer gets MagicLink -->verify
|
|
87
|
-
verify-- access private API -->validate[4. session validation]
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
### Prerequisites
|
|
91
|
-
|
|
92
|
-
Replace any instance of `<ProjectID>` in the code below with your company's Project ID, which can be found in the [Descope console](https://app.descope.com).
|
|
93
|
-
|
|
94
|
-
* Run the following commands in your project
|
|
95
|
-
|
|
96
|
-
These commands will add the Descope NodeJS SDK as a project dependency.
|
|
97
|
-
|
|
98
|
-
```bash
|
|
99
|
-
npm i --save @descope/node-sdk
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
* Import and initialize the ExpresSDK for NodeJS client in your source code
|
|
103
|
-
|
|
104
|
-
```javascript
|
|
105
|
-
import DescopeClient from '@descope/node-sdk';
|
|
106
|
-
const descopeClient = DescopeClient({ projectId: <ProjectID> });
|
|
107
|
-
```
|
|
108
|
-
or
|
|
109
|
-
|
|
110
|
-
```javascript
|
|
111
|
-
const sdk = require('@descope/node-sdk');
|
|
112
|
-
const descopeClient = sdk({ projectId: <ProjectID> });
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### 1. Customer Sign-up
|
|
116
|
-
|
|
117
|
-
In your sign-up route using magic link (for example, `myapp.com/signup`) generate a sign-up request and send the magic link via the selected delivery method. In the example below an email is sent to "mytestmail@test.com" containing the magic link and the link will automatically return back to the provided URL ("https://mydomain.com/verify"). In additon, optional user data (for exmaple, a custom username in the code sample below) can be gathered during the sign-up process.
|
|
118
|
-
|
|
119
|
-
```javascript
|
|
120
|
-
await descopeClient.magiclink.signUp.email("mytestmail@test.com", { name: "custom name" })
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### 2. Customer Sign-in
|
|
124
|
-
In your sign-in route using magic link (for exmaple, `myapp.com/login`) generate a sign-in request send the magic link via the selected delivery method. In the example below an email is sent to "mytestmail@test.com" containing the magic link and the link will automatically return back to the provided URL ("https://mydomain.com/verify").
|
|
125
|
-
|
|
126
|
-
```javascript
|
|
127
|
-
await descopeClient.magiclink.signIn.email("mytestmail@test.com")
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### 3. Customer Verification
|
|
131
|
-
|
|
132
|
-
In your verify customer route for magic link (for example, `mydomain.com/verify`) verify the token from either a customer sign-up or sign-in.
|
|
133
|
-
|
|
134
|
-
```javascript
|
|
135
|
-
const out = await descopeClient.magiclink.verify(token)
|
|
136
|
-
if (out.data.cookies) {
|
|
137
|
-
res.set('Set-Cookie', out.data.cookies)
|
|
138
|
-
}
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### 4. Session Validation
|
|
142
|
-
|
|
143
|
-
Session validation checks to see that the visitor to your website or application is who they say they are, by comparing the value in the validation variables against the session data that is already stored.
|
|
144
|
-
|
|
145
|
-
```javascript
|
|
146
|
-
const out = await descopeClient.validateSession(session_jwt, refresh_jwt)
|
|
147
|
-
if (out?.cookies) {
|
|
148
|
-
res.set('Set-Cookie', out.cookies)
|
|
149
|
-
}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
## ExpressStart with OAuth Authentication
|
|
153
|
-
|
|
154
|
-
In the example below, we assume using the Descope builtin oauth provider, in that case, we dont need to define any specific application details.
|
|
155
|
-
|
|
156
|
-
### Prerequisites
|
|
157
|
-
|
|
158
|
-
Replace any instance of `<ProjectID>` in the code below with your company's Project ID, which can be found in the [Descope console](https://app.descope.com).
|
|
159
|
-
|
|
160
|
-
* Run the following commands in your project
|
|
161
|
-
|
|
162
|
-
These commands will add the Descope NodeJS SDK as a project dependency.
|
|
163
|
-
|
|
164
|
-
```bash
|
|
165
|
-
npm i --save @descope/node-sdk
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
* Import and initialize the ExpresSDK for NodeJS client in your source code
|
|
169
|
-
|
|
170
|
-
```javascript
|
|
171
|
-
import DescopeClient from '@descope/node-sdk';
|
|
172
|
-
const descopeClient = DescopeClient({ projectId: <ProjectID> });
|
|
173
|
-
```
|
|
174
|
-
or
|
|
175
|
-
|
|
176
|
-
```javascript
|
|
177
|
-
const sdk = require('@descope/node-sdk');
|
|
178
|
-
const descopeClient = sdk({ projectId: <ProjectID> });
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
* Make sure to set the return url to exchange in the Descope Oauth authentication methods settings or use the start first argument.
|
|
182
|
-
|
|
183
|
-
### 1. Customer Sign-up/Sign-In
|
|
184
|
-
|
|
185
|
-
In your OAuth start flow (for example, `myapp.com/login-with-facebook`) generate a url to redirect the user to. In the example below the login
|
|
186
|
-
|
|
187
|
-
```javascript
|
|
188
|
-
const out = await descopeClient.oauth.start.facebook();
|
|
189
|
-
return out.data.url;
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
### 2. Customer Exchange
|
|
193
|
-
|
|
194
|
-
In your exchange for any of the oauth provider (for example, `mydomain.com/exchange`) verify the code from the provider by using the exchange method.
|
|
195
|
-
|
|
196
|
-
```javascript
|
|
197
|
-
const code = req.query.code
|
|
198
|
-
const out = await descopeClient.oauth.exchnage(code);
|
|
199
|
-
if (out.data.cookies) {
|
|
200
|
-
res.set('Set-Cookie', out.data.cookies);
|
|
201
|
-
}
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
### 3. Session Validation
|
|
205
|
-
|
|
206
|
-
Session validation checks to see that the visitor to your website or application is who they say they are, by comparing the value in the validation variables against the session data that is already stored.
|
|
207
|
-
|
|
208
|
-
```javascript
|
|
209
|
-
const out = await descopeClient.validateSession(session_jwt, refresh_jwt);
|
|
210
|
-
if (out.cookies) {
|
|
211
|
-
res.set('Set-Cookie', out.cookies);
|
|
212
|
-
}
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
### 4. Error handling
|
|
216
|
-
|
|
217
|
-
Each authentication function may return an error upon authentication failure or bad request. In case of an error, you will recieve an sdk response with `ok` equal to false and the error details are also included, such as the `errorCode` and the `errorDescription`. Needless to say, the `data` will be empty whenever an error occur.
|
|
218
|
-
|
|
219
|
-
```javascript
|
|
220
|
-
const out = await descopeClient.oauth.start.facebook();
|
|
221
|
-
if (!out.ok) {
|
|
222
|
-
console.log(`an error has occured [code: ${out.errorCode}] with message: "${out.errorDescription}"`)
|
|
223
|
-
} else {
|
|
224
|
-
...
|
|
225
|
-
}
|
|
226
|
-
```
|
|
19
|
+
- [x] [One time passwords (OTP)](./docs/otp.md)
|
|
20
|
+
- [x] [Magic Links](./docs/magiclink.md)
|
|
21
|
+
- [x] [OAuth/Social](./docs/oauth.md)
|
|
227
22
|
|
|
228
23
|
## Run the Examples
|
|
229
24
|
|
|
@@ -267,4 +62,4 @@ Run the following commands in the root of the project to build and run the examp
|
|
|
267
62
|
|
|
268
63
|
## License
|
|
269
64
|
|
|
270
|
-
The Descope ExpresSDK for
|
|
65
|
+
The Descope ExpresSDK for Node is licensed for use under the terms and conditions of the [MIT license Agreement](https://github.com/descope/node-sdk/blob/main/LICENSE).
|
package/dist/cjs/index.cjs.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var e=require("
|
|
1
|
+
"use strict";var e=require("@descope/core-js-sdk"),t=require("jose"),s=require("node-fetch"),i=require("tslib");function o(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var a=o(e),r=o(s);const n=(e,t,s)=>`${e}=${t}; Domain=${(null==s?void 0:s.cookieDomain)||""}; Max-Age=${(null==s?void 0:s.cookieMaxAge)||""}; Path=${(null==s?void 0:s.cookiePath)||"/"}; HttpOnly; SameSite=Strict`,c=e=>async(...t)=>{var s,o,a;const r=await e(...t);if(!r.data)return r;let c=r.data,{sessionJwt:l,refreshJwt:h}=c,d=i.__rest(c,["sessionJwt","refreshJwt"]);const u=[n("DS",l,d)];return h?u.push(n("DSR",h,d)):(null===(s=r.response)||void 0===s?void 0:s.headers.get("set-cookie"))&&(h=((e,t)=>{const s=null==e?void 0:e.match(RegExp(`(?:^|;\\s*)${t}=([^;]*)`));return s?s[1]:null})(null===(o=r.response)||void 0===o?void 0:o.headers.get("set-cookie"),"DSR"),u.push(null===(a=r.response)||void 0===a?void 0:a.headers.get("set-cookie"))),Object.assign(Object.assign({},r),{data:Object.assign(Object.assign({},r.data),{refreshJwt:h,cookies:u})})},l=(e,t,s)=>{if(!e)return;const i="string"==typeof t?t.split("."):t,o=i.shift()||"";if(0===i.length||"*"===o){const t=t=>{if(!t||"function"!=typeof e[t])throw Error(`cannot wrap value at key "${t.toString()}"`);e[t]=s(e[t])};"*"===o?Object.keys(e).forEach(t):t(o)}else l(e[o],i,s)};globalThis.fetch||(globalThis.fetch=r.default,globalThis.Headers=s.Headers,globalThis.Request=s.Request,globalThis.Response=s.Response);const h=(...e)=>{const s=a.default(...e);var i,o;i=s,o=c,["otp.verify.*","magicLink.verify","magicLink.crossDevice.signUp.*","magicLink.crossDevice.signIn.*","oauth.exchange","saml.exchange","totp.verify","webauthn.signIn.finish","webauthn.signUp.finish","refresh"].forEach((e=>l(i,e,o)));const{projectId:r,logger:n}=e[0],h={};return Object.assign(Object.assign({},s),{async getKey(e){if(!(null==e?void 0:e.kid))throw Error("header.kid must not be empty");if(h[e.kid])return h[e.kid];if(Object.assign(h,await(async()=>{const e=await s.httpClient.get(`v1/keys/${r}`).then((e=>e.json()));return Array.isArray(e)?(await Promise.all(e.map((async e=>[e.kid,await t.importJWK(e)])))).reduce(((e,[t,s])=>t?Object.assign(Object.assign({},e),{[t.toString()]:s}):e),{}):{}})()),!h[e.kid])throw Error("failed to fetch matching key");return h[e.kid]},async validateToken(e){return{token:(await t.jwtVerify(e,this.getKey,{algorithms:["ES384"]})).payload}},async validateSession(e,t){if(!e)throw Error("session token must not be empty");try{return await this.validateToken(e)}catch(e){try{return await this.validateToken(t),(await this.refresh(t)).data}catch(e){throw null==n||n.error("failed to validate refresh token",e),Error("could not validate tokens")}}}})};h.DeliveryMethods=a.default.DeliveryMethods,h.RefreshTokenCookieName="DSR",h.SessionTokenCookieName="DS",module.exports=h;
|
|
2
2
|
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../../lib/constants.ts","../../lib/helpers.ts","../../lib/index.ts"],"sourcesContent":["// eslint-disable-next-line import/prefer-default-export\nexport const refreshTokenCookieName = 'DSR'\nexport const sessionTokenCookieName = 'DS'\n","import type { SdkResponse } from '@descope/core-js-sdk'\nimport { refreshTokenCookieName, sessionTokenCookieName } from './constants'\n\nconst generateCookie = (name: string, value: string)
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../../lib/constants.ts","../../lib/helpers.ts","../../lib/index.ts"],"sourcesContent":["// eslint-disable-next-line import/prefer-default-export\nexport const refreshTokenCookieName = 'DSR'\nexport const sessionTokenCookieName = 'DS'\n","import type { SdkResponse } from '@descope/core-js-sdk'\nimport { refreshTokenCookieName, sessionTokenCookieName } from './constants'\n\nconst generateCookie = (name: string, value: string, options?: any) =>\n `${name}=${value}; Domain=${options?.cookieDomain || ''}; Max-Age=${\n options?.cookieMaxAge || ''\n }; Path=${options?.cookiePath || '/'}; HttpOnly; SameSite=Strict`\n\nconst getCookieValue = (cookie: string | null | undefined, name: string) => {\n const match = cookie?.match(RegExp(`(?:^|;\\\\s*)${name}=([^;]*)`))\n return match ? match[1] : null\n}\n\n// eslint-disable-next-line import/prefer-default-export\nexport const withCookie =\n <T extends Array<any>, U extends Promise<SdkResponse>>(fn: (...args: T) => U) =>\n async (...args: T): Promise<SdkResponse> => {\n const resp = await fn(...args)\n\n if (!resp.data) {\n return resp\n }\n\n // eslint-disable-next-line prefer-const\n let { sessionJwt, refreshJwt, ...rest } = resp.data\n const cookies = [generateCookie(sessionTokenCookieName, sessionJwt, rest)]\n\n if (!refreshJwt) {\n if (resp.response?.headers.get('set-cookie')) {\n refreshJwt = getCookieValue(\n resp.response?.headers.get('set-cookie'),\n refreshTokenCookieName,\n )\n cookies.push(resp.response?.headers.get('set-cookie')!)\n }\n } else {\n cookies.push(generateCookie(refreshTokenCookieName, refreshJwt, rest))\n }\n\n return { ...resp, data: { ...resp.data, refreshJwt, cookies } }\n }\n\nexport const wrapWith = <T extends Record<string, any>>(\n obj: T,\n path: string | string[],\n wrappingFn: Function,\n // eslint-disable-next-line consistent-return\n): void => {\n if (!obj) return\n\n const pathSections = typeof path === 'string' ? path.split('.') : path\n const section = pathSections.shift() || ('' as keyof T)\n\n if (pathSections.length === 0 || section === '*') {\n const wrap = (key: keyof T) => {\n if (key && typeof obj[key] === 'function') {\n // eslint-disable-next-line no-param-reassign\n obj[key] = wrappingFn(obj[key])\n } else {\n throw Error(`cannot wrap value at key \"${key.toString()}\"`)\n }\n }\n if (section === '*') {\n Object.keys(obj).forEach(wrap)\n } else {\n wrap(section)\n }\n } else {\n wrapWith(obj[section], pathSections, wrappingFn)\n }\n}\n\nexport const bulkWrapWith = (\n obj: Parameters<typeof wrapWith>[0],\n paths: string[],\n wrappingFn: Parameters<typeof wrapWith>[2],\n) => paths.forEach((path: string) => wrapWith(obj, path, wrappingFn))\n","import createSdk from '@descope/core-js-sdk'\nimport { KeyLike, jwtVerify, JWK, JWTHeaderParameters, importJWK } from 'jose'\nimport fetch, { Headers, Response, Request } from 'node-fetch'\nimport { bulkWrapWith, withCookie } from './helpers'\nimport { AuthenticationInfo } from './types'\nimport { refreshTokenCookieName, sessionTokenCookieName } from './constants'\n\nif (!globalThis.fetch) {\n // @ts-ignore\n globalThis.fetch = fetch\n // @ts-ignore\n globalThis.Headers = Headers\n // @ts-ignore\n globalThis.Request = Request\n // @ts-ignore\n globalThis.Response = Response\n}\n\nconst sdk = (...args: Parameters<typeof createSdk>) => {\n const coreSdk = createSdk(...args)\n\n bulkWrapWith(\n coreSdk,\n [\n 'otp.verify.*',\n 'magicLink.verify',\n 'magicLink.crossDevice.signUp.*',\n 'magicLink.crossDevice.signIn.*',\n 'oauth.exchange',\n 'saml.exchange',\n 'totp.verify',\n 'webauthn.signIn.finish',\n 'webauthn.signUp.finish',\n 'refresh',\n ],\n withCookie,\n )\n\n const { projectId, logger } = args[0]\n\n const keys: Record<string, KeyLike | Uint8Array> = {}\n\n const fetchKeys = async () => {\n const publicKeys: JWK[] = await coreSdk.httpClient\n .get(`v1/keys/${projectId}`)\n .then((resp) => resp.json())\n if (!Array.isArray(publicKeys)) return {}\n const kidJwksPairs = await Promise.all(\n publicKeys.map(async (key) => [key.kid, await importJWK(key)]),\n )\n\n return kidJwksPairs.reduce(\n (acc, [kid, jwk]) => (kid ? { ...acc, [kid.toString()]: jwk } : acc),\n {},\n )\n }\n\n return {\n ...coreSdk,\n\n async getKey(header: JWTHeaderParameters): Promise<KeyLike | Uint8Array> {\n if (!header?.kid) throw Error('header.kid must not be empty')\n\n if (keys[header.kid]) return keys[header.kid]\n\n // do we need to fetch once or every time?\n Object.assign(keys, await fetchKeys())\n\n if (!keys[header.kid]) throw Error('failed to fetch matching key')\n\n return keys[header.kid]\n },\n\n async validateToken(token: string): Promise<AuthenticationInfo> {\n const res = await jwtVerify(token, this.getKey, { algorithms: ['ES384'] })\n\n return { token: res.payload }\n },\n\n async validateSession(\n sessionToken: string,\n refreshToken: string,\n ): Promise<AuthenticationInfo | undefined> {\n if (!sessionToken) throw Error('session token must not be empty')\n\n try {\n const token = await this.validateToken(sessionToken)\n return token\n } catch (error) {\n try {\n await this.validateToken(refreshToken)\n\n return (await this.refresh(refreshToken)).data\n } catch (refreshTokenErr) {\n logger?.error('failed to validate refresh token', refreshTokenErr)\n\n throw Error('could not validate tokens')\n }\n }\n },\n }\n}\n\nconst sdkWithAttributes = sdk as typeof sdk & {\n DeliveryMethods: typeof createSdk.DeliveryMethods\n RefreshTokenCookieName: typeof refreshTokenCookieName\n SessionTokenCookieName: typeof sessionTokenCookieName\n}\n\nsdkWithAttributes.DeliveryMethods = createSdk.DeliveryMethods\nsdkWithAttributes.RefreshTokenCookieName = refreshTokenCookieName\nsdkWithAttributes.SessionTokenCookieName = sessionTokenCookieName\n\nexport default sdkWithAttributes\n\nexport type { DeliveryMethod, OAuthProvider } from '@descope/core-js-sdk'\n"],"names":["generateCookie","name","value","options","cookieDomain","cookieMaxAge","cookiePath","withCookie","fn","async","args","resp","data","_d","sessionJwt","refreshJwt","rest","__rest","cookies","push","_a","response","headers","get","cookie","match","RegExp","getCookieValue","_b","_c","Object","assign","wrapWith","obj","path","wrappingFn","pathSections","split","section","shift","length","wrap","key","Error","toString","keys","forEach","globalThis","fetch","Headers","Request","Response","sdkWithAttributes","coreSdk","createSdk","projectId","logger","header","kid","publicKeys","httpClient","then","json","Array","isArray","Promise","all","map","importJWK","reduce","acc","jwk","fetchKeys","token","jwtVerify","this","getKey","algorithms","payload","sessionToken","refreshToken","validateToken","error","refresh","refreshTokenErr","DeliveryMethods","RefreshTokenCookieName","SessionTokenCookieName"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,9 @@ interface AuthenticationInfo {
|
|
|
13
13
|
cookies?: string[];
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
declare const refreshTokenCookieName = "DSR";
|
|
17
|
+
declare const sessionTokenCookieName = "DS";
|
|
18
|
+
|
|
16
19
|
declare const sdkWithAttributes: ((args_0: {
|
|
17
20
|
projectId: string;
|
|
18
21
|
logger?: {
|
|
@@ -261,6 +264,8 @@ declare const sdkWithAttributes: ((args_0: {
|
|
|
261
264
|
};
|
|
262
265
|
}) & {
|
|
263
266
|
DeliveryMethods: typeof _descope_core_js_sdk__default.DeliveryMethods;
|
|
267
|
+
RefreshTokenCookieName: typeof refreshTokenCookieName;
|
|
268
|
+
SessionTokenCookieName: typeof sessionTokenCookieName;
|
|
264
269
|
};
|
|
265
270
|
|
|
266
271
|
export { sdkWithAttributes as default };
|
package/dist/index.esm.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
1
|
+
import e from"@descope/core-js-sdk";import{jwtVerify as t,importJWK as i}from"jose";import o,{Headers as s,Request as a,Response as r}from"node-fetch";import{__rest as n}from"tslib";const c=(e,t,i)=>`${e}=${t}; Domain=${(null==i?void 0:i.cookieDomain)||""}; Max-Age=${(null==i?void 0:i.cookieMaxAge)||""}; Path=${(null==i?void 0:i.cookiePath)||"/"}; HttpOnly; SameSite=Strict`,l=e=>async(...t)=>{var i,o,s;const a=await e(...t);if(!a.data)return a;let r=a.data,{sessionJwt:l,refreshJwt:h}=r,d=n(r,["sessionJwt","refreshJwt"]);const g=[c("DS",l,d)];return h?g.push(c("DSR",h,d)):(null===(i=a.response)||void 0===i?void 0:i.headers.get("set-cookie"))&&(h=((e,t)=>{const i=null==e?void 0:e.match(RegExp(`(?:^|;\\s*)${t}=([^;]*)`));return i?i[1]:null})(null===(o=a.response)||void 0===o?void 0:o.headers.get("set-cookie"),"DSR"),g.push(null===(s=a.response)||void 0===s?void 0:s.headers.get("set-cookie"))),Object.assign(Object.assign({},a),{data:Object.assign(Object.assign({},a.data),{refreshJwt:h,cookies:g})})},h=(e,t,i)=>{if(!e)return;const o="string"==typeof t?t.split("."):t,s=o.shift()||"";if(0===o.length||"*"===s){const t=t=>{if(!t||"function"!=typeof e[t])throw Error(`cannot wrap value at key "${t.toString()}"`);e[t]=i(e[t])};"*"===s?Object.keys(e).forEach(t):t(s)}else h(e[s],o,i)};globalThis.fetch||(globalThis.fetch=o,globalThis.Headers=s,globalThis.Request=a,globalThis.Response=r);const d=(...o)=>{const s=e(...o);var a,r;a=s,r=l,["otp.verify.*","magicLink.verify","magicLink.crossDevice.signUp.*","magicLink.crossDevice.signIn.*","oauth.exchange","saml.exchange","totp.verify","webauthn.signIn.finish","webauthn.signUp.finish","refresh"].forEach((e=>h(a,e,r)));const{projectId:n,logger:c}=o[0],d={};return Object.assign(Object.assign({},s),{async getKey(e){if(!(null==e?void 0:e.kid))throw Error("header.kid must not be empty");if(d[e.kid])return d[e.kid];if(Object.assign(d,await(async()=>{const e=await s.httpClient.get(`v1/keys/${n}`).then((e=>e.json()));return Array.isArray(e)?(await Promise.all(e.map((async e=>[e.kid,await i(e)])))).reduce(((e,[t,i])=>t?Object.assign(Object.assign({},e),{[t.toString()]:i}):e),{}):{}})()),!d[e.kid])throw Error("failed to fetch matching key");return d[e.kid]},async validateToken(e){return{token:(await t(e,this.getKey,{algorithms:["ES384"]})).payload}},async validateSession(e,t){if(!e)throw Error("session token must not be empty");try{return await this.validateToken(e)}catch(e){try{return await this.validateToken(t),(await this.refresh(t)).data}catch(e){throw null==c||c.error("failed to validate refresh token",e),Error("could not validate tokens")}}}})};d.DeliveryMethods=e.DeliveryMethods,d.RefreshTokenCookieName="DSR",d.SessionTokenCookieName="DS";export{d as default};
|
|
2
2
|
//# sourceMappingURL=index.esm.js.map
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../lib/constants.ts","../lib/helpers.ts","../lib/index.ts"],"sourcesContent":["// eslint-disable-next-line import/prefer-default-export\nexport const refreshTokenCookieName = 'DSR'\nexport const sessionTokenCookieName = 'DS'\n","import type { SdkResponse } from '@descope/core-js-sdk'\nimport { refreshTokenCookieName, sessionTokenCookieName } from './constants'\n\nconst generateCookie = (name: string, value: string)
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../lib/constants.ts","../lib/helpers.ts","../lib/index.ts"],"sourcesContent":["// eslint-disable-next-line import/prefer-default-export\nexport const refreshTokenCookieName = 'DSR'\nexport const sessionTokenCookieName = 'DS'\n","import type { SdkResponse } from '@descope/core-js-sdk'\nimport { refreshTokenCookieName, sessionTokenCookieName } from './constants'\n\nconst generateCookie = (name: string, value: string, options?: any) =>\n `${name}=${value}; Domain=${options?.cookieDomain || ''}; Max-Age=${\n options?.cookieMaxAge || ''\n }; Path=${options?.cookiePath || '/'}; HttpOnly; SameSite=Strict`\n\nconst getCookieValue = (cookie: string | null | undefined, name: string) => {\n const match = cookie?.match(RegExp(`(?:^|;\\\\s*)${name}=([^;]*)`))\n return match ? match[1] : null\n}\n\n// eslint-disable-next-line import/prefer-default-export\nexport const withCookie =\n <T extends Array<any>, U extends Promise<SdkResponse>>(fn: (...args: T) => U) =>\n async (...args: T): Promise<SdkResponse> => {\n const resp = await fn(...args)\n\n if (!resp.data) {\n return resp\n }\n\n // eslint-disable-next-line prefer-const\n let { sessionJwt, refreshJwt, ...rest } = resp.data\n const cookies = [generateCookie(sessionTokenCookieName, sessionJwt, rest)]\n\n if (!refreshJwt) {\n if (resp.response?.headers.get('set-cookie')) {\n refreshJwt = getCookieValue(\n resp.response?.headers.get('set-cookie'),\n refreshTokenCookieName,\n )\n cookies.push(resp.response?.headers.get('set-cookie')!)\n }\n } else {\n cookies.push(generateCookie(refreshTokenCookieName, refreshJwt, rest))\n }\n\n return { ...resp, data: { ...resp.data, refreshJwt, cookies } }\n }\n\nexport const wrapWith = <T extends Record<string, any>>(\n obj: T,\n path: string | string[],\n wrappingFn: Function,\n // eslint-disable-next-line consistent-return\n): void => {\n if (!obj) return\n\n const pathSections = typeof path === 'string' ? path.split('.') : path\n const section = pathSections.shift() || ('' as keyof T)\n\n if (pathSections.length === 0 || section === '*') {\n const wrap = (key: keyof T) => {\n if (key && typeof obj[key] === 'function') {\n // eslint-disable-next-line no-param-reassign\n obj[key] = wrappingFn(obj[key])\n } else {\n throw Error(`cannot wrap value at key \"${key.toString()}\"`)\n }\n }\n if (section === '*') {\n Object.keys(obj).forEach(wrap)\n } else {\n wrap(section)\n }\n } else {\n wrapWith(obj[section], pathSections, wrappingFn)\n }\n}\n\nexport const bulkWrapWith = (\n obj: Parameters<typeof wrapWith>[0],\n paths: string[],\n wrappingFn: Parameters<typeof wrapWith>[2],\n) => paths.forEach((path: string) => wrapWith(obj, path, wrappingFn))\n","import createSdk from '@descope/core-js-sdk'\nimport { KeyLike, jwtVerify, JWK, JWTHeaderParameters, importJWK } from 'jose'\nimport fetch, { Headers, Response, Request } from 'node-fetch'\nimport { bulkWrapWith, withCookie } from './helpers'\nimport { AuthenticationInfo } from './types'\nimport { refreshTokenCookieName, sessionTokenCookieName } from './constants'\n\nif (!globalThis.fetch) {\n // @ts-ignore\n globalThis.fetch = fetch\n // @ts-ignore\n globalThis.Headers = Headers\n // @ts-ignore\n globalThis.Request = Request\n // @ts-ignore\n globalThis.Response = Response\n}\n\nconst sdk = (...args: Parameters<typeof createSdk>) => {\n const coreSdk = createSdk(...args)\n\n bulkWrapWith(\n coreSdk,\n [\n 'otp.verify.*',\n 'magicLink.verify',\n 'magicLink.crossDevice.signUp.*',\n 'magicLink.crossDevice.signIn.*',\n 'oauth.exchange',\n 'saml.exchange',\n 'totp.verify',\n 'webauthn.signIn.finish',\n 'webauthn.signUp.finish',\n 'refresh',\n ],\n withCookie,\n )\n\n const { projectId, logger } = args[0]\n\n const keys: Record<string, KeyLike | Uint8Array> = {}\n\n const fetchKeys = async () => {\n const publicKeys: JWK[] = await coreSdk.httpClient\n .get(`v1/keys/${projectId}`)\n .then((resp) => resp.json())\n if (!Array.isArray(publicKeys)) return {}\n const kidJwksPairs = await Promise.all(\n publicKeys.map(async (key) => [key.kid, await importJWK(key)]),\n )\n\n return kidJwksPairs.reduce(\n (acc, [kid, jwk]) => (kid ? { ...acc, [kid.toString()]: jwk } : acc),\n {},\n )\n }\n\n return {\n ...coreSdk,\n\n async getKey(header: JWTHeaderParameters): Promise<KeyLike | Uint8Array> {\n if (!header?.kid) throw Error('header.kid must not be empty')\n\n if (keys[header.kid]) return keys[header.kid]\n\n // do we need to fetch once or every time?\n Object.assign(keys, await fetchKeys())\n\n if (!keys[header.kid]) throw Error('failed to fetch matching key')\n\n return keys[header.kid]\n },\n\n async validateToken(token: string): Promise<AuthenticationInfo> {\n const res = await jwtVerify(token, this.getKey, { algorithms: ['ES384'] })\n\n return { token: res.payload }\n },\n\n async validateSession(\n sessionToken: string,\n refreshToken: string,\n ): Promise<AuthenticationInfo | undefined> {\n if (!sessionToken) throw Error('session token must not be empty')\n\n try {\n const token = await this.validateToken(sessionToken)\n return token\n } catch (error) {\n try {\n await this.validateToken(refreshToken)\n\n return (await this.refresh(refreshToken)).data\n } catch (refreshTokenErr) {\n logger?.error('failed to validate refresh token', refreshTokenErr)\n\n throw Error('could not validate tokens')\n }\n }\n },\n }\n}\n\nconst sdkWithAttributes = sdk as typeof sdk & {\n DeliveryMethods: typeof createSdk.DeliveryMethods\n RefreshTokenCookieName: typeof refreshTokenCookieName\n SessionTokenCookieName: typeof sessionTokenCookieName\n}\n\nsdkWithAttributes.DeliveryMethods = createSdk.DeliveryMethods\nsdkWithAttributes.RefreshTokenCookieName = refreshTokenCookieName\nsdkWithAttributes.SessionTokenCookieName = sessionTokenCookieName\n\nexport default sdkWithAttributes\n\nexport type { DeliveryMethod, OAuthProvider } from '@descope/core-js-sdk'\n"],"names":["generateCookie","name","value","options","cookieDomain","cookieMaxAge","cookiePath","withCookie","fn","async","args","resp","data","_d","sessionJwt","refreshJwt","rest","__rest","cookies","push","_a","response","headers","get","cookie","match","RegExp","getCookieValue","_b","_c","Object","assign","wrapWith","obj","path","wrappingFn","pathSections","split","section","shift","length","wrap","key","Error","toString","keys","forEach","globalThis","fetch","Headers","Request","Response","sdkWithAttributes","coreSdk","createSdk","projectId","logger","header","kid","publicKeys","httpClient","then","json","Array","isArray","Promise","all","map","importJWK","reduce","acc","jwk","fetchKeys","token","jwtVerify","this","getKey","algorithms","payload","sessionToken","refreshToken","validateToken","error","refresh","refreshTokenErr","DeliveryMethods","RefreshTokenCookieName","SessionTokenCookieName"],"mappings":"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"}
|