Comparing version 3.12.1 to 3.13.0
@@ -8,2 +8,6 @@ "use strict"; | ||
var _url = require("url"); | ||
var jose = _interopRequireWildcard(require("jose")); | ||
var _axios = _interopRequireDefault(require("axios")); | ||
@@ -31,2 +35,4 @@ | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } | ||
@@ -36,4 +42,2 @@ | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
const DEFAULT_TIMEOUT = 10 * 60 * 1000; // Ten minutes | ||
@@ -73,3 +77,16 @@ | ||
} | ||
}); | ||
}); // Get a baseURL that ends with a slash to make building route URLs easier. | ||
let baseURL = config.env; | ||
if (!baseURL.endsWith("/")) { | ||
baseURL += "/"; | ||
} | ||
const jwtConfig = { | ||
// Only allow JWTs that were meant for this project. | ||
projectID: config.project_id, | ||
// Fetch the signature verification keys for this project as needed. | ||
jwks: jose.createRemoteJWKSet(new _url.URL(`sessions/jwks/${config.project_id}`, baseURL)) | ||
}; | ||
this.users = new _users.Users(this.client); | ||
@@ -79,3 +96,3 @@ this.magicLinks = new _magic_links.MagicLinks(this.client); | ||
this.otps = new _otps.OTPs(this.client); | ||
this.sessions = new _sessions.Sessions(this.client); | ||
this.sessions = new _sessions.Sessions(this.client, jwtConfig); | ||
this.totps = new _totps.TOTPs(this.client); | ||
@@ -82,0 +99,0 @@ this.webauthn = new _webauthn.WebAuthn(this.client); |
@@ -6,3 +6,3 @@ "use strict"; | ||
}); | ||
exports.RequestError = exports.StytchError = void 0; | ||
exports.ClientError = exports.RequestError = exports.StytchError = void 0; | ||
@@ -31,2 +31,19 @@ class StytchError extends Error { | ||
exports.RequestError = RequestError; | ||
exports.RequestError = RequestError; | ||
class ClientError extends Error { | ||
constructor(code, message, cause) { | ||
let msg = `${code}: ${message}`; | ||
if (cause) { | ||
msg += `: ${cause}`; | ||
} | ||
super(msg); | ||
this.code = code; | ||
this.cause = cause; | ||
} | ||
} | ||
exports.ClientError = ClientError; |
@@ -8,11 +8,27 @@ "use strict"; | ||
var jose = _interopRequireWildcard(require("jose")); | ||
var _shared = require("./shared"); | ||
var _errors = require("./errors"); | ||
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } | ||
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
const sessionClaim = "https://stytch.com/session"; | ||
class Sessions { | ||
constructor(client) { | ||
constructor(client, jwtConfig) { | ||
_defineProperty(this, "base_path", "sessions"); | ||
this.client = client; | ||
this.jwksClient = jwtConfig.jwks; | ||
this.jwtOptions = { | ||
audience: jwtConfig.projectID, | ||
issuer: `stytch.com/${jwtConfig.projectID}`, | ||
typ: "JWT" | ||
}; | ||
} | ||
@@ -36,2 +52,9 @@ | ||
jwks(project_id) { | ||
return (0, _shared.request)(this.client, { | ||
method: "GET", | ||
url: this.endpoint(`jwks/${project_id}`) | ||
}); | ||
} | ||
authenticate(data) { | ||
@@ -48,3 +71,93 @@ return (0, _shared.request)(this.client, { | ||
} | ||
/** Parse a JWT and verify the signature, preferring local verification over remote. | ||
* | ||
* If max_token_age_seconds is set, remote verification will be forced if the JWT was issued at | ||
* (based on the "iat" claim) more than that many seconds ago. | ||
* | ||
* To force remote validation for all tokens, set max_token_age_seconds to zero or use the | ||
* authenticate method instead. | ||
*/ | ||
async authenticateJwt(jwt, options) { | ||
try { | ||
const session = await this.authenticateJwtLocal(jwt, options); | ||
return { | ||
session, | ||
session_jwt: jwt | ||
}; | ||
} catch (err) { | ||
if (err instanceof _errors.ClientError && err.code === "jwt_too_old") { | ||
// JWT was too old (stale) to verify locally. Check with the Stytch API. | ||
return this.authenticate({ | ||
session_jwt: jwt | ||
}); | ||
} | ||
throw err; | ||
} | ||
} | ||
/** Parse a JWT and verify the signature locally (without calling /authenticate in the API). | ||
* | ||
* If maxTokenAge is set, this will return an error if the JWT was issued (based on the "iat" | ||
* claim) more than maxTokenAge seconds ago. | ||
* | ||
* If max_token_age_seconds is explicitly set to zero, all tokens will be considered too old, | ||
* even if they are otherwise valid. | ||
* | ||
* The value for current_date is used to compare timestamp claims ("exp", "nbf", "iat"). It | ||
* defaults to the current date (new Date()). | ||
* | ||
* The value for clock_tolerance_seconds is the maximum allowable difference when comparing | ||
* timestamps. It defaults to zero. | ||
*/ | ||
async authenticateJwtLocal(jwt, options) { | ||
const now = (options === null || options === void 0 ? void 0 : options.current_date) || new Date(); | ||
let payload; | ||
try { | ||
const token = await jose.jwtVerify(jwt, this.jwksClient, { ...this.jwtOptions, | ||
clockTolerance: options === null || options === void 0 ? void 0 : options.clock_tolerance_seconds, | ||
currentDate: now // Don't pass maxTokenAge directly to jwtVerify because it interprets zero as "infinity". | ||
// We want zero to mean "every token is stale" and force remote verification. | ||
}); | ||
payload = token.payload; | ||
} catch (err) { | ||
throw new _errors.ClientError("jwt_invalid", "Could not verify JWT", err); | ||
} | ||
const maxTokenAge = options === null || options === void 0 ? void 0 : options.max_token_age_seconds; | ||
if (maxTokenAge != null) { | ||
const iat = payload.iat; | ||
if (!iat) { | ||
throw new _errors.ClientError("jwt_invalid", "JWT was missing iat claim"); | ||
} | ||
const nowEpoch = +now / 1000; // Epoch seconds from milliseconds | ||
if (nowEpoch - iat >= maxTokenAge) { | ||
throw new _errors.ClientError("jwt_too_old", `JWT was issued at ${iat}, more than ${maxTokenAge} seconds ago`); | ||
} | ||
} | ||
const claim = payload[sessionClaim]; | ||
return { | ||
session_id: claim.id, | ||
attributes: claim.attributes, | ||
authentication_factors: claim.authentication_factors, | ||
user_id: payload.sub || "", | ||
// Parse the timestamps into Dates. The JWT expiration time is the same as the session's. | ||
// The exp claim is a Unix timestamp in seconds, so convert it to milliseconds first. The | ||
// other two timestamps are RFC3339-formatted strings. | ||
started_at: new Date(claim.started_at), | ||
last_accessed_at: new Date(claim.last_accessed_at), | ||
expires_at: new Date((payload.exp || 0) * 1000) | ||
}; | ||
} | ||
revoke(data) { | ||
@@ -51,0 +164,0 @@ return (0, _shared.request)(this.client, { |
{ | ||
"name": "stytch", | ||
"version": "3.12.1", | ||
"version": "3.13.0", | ||
"description": "A wrapper for the Stytch API", | ||
@@ -49,3 +49,4 @@ "types": "./types/lib/index.d.ts", | ||
"dependencies": { | ||
"axios": "^0.21.4" | ||
"axios": "^0.21.4", | ||
"jose": "^4.6.0" | ||
}, | ||
@@ -52,0 +53,0 @@ "eslintConfig": { |
@@ -20,2 +20,3 @@ import { Session } from "./shared"; | ||
session_token?: string; | ||
session_jwt?: string; | ||
session_duration_minutes?: number; | ||
@@ -26,2 +27,3 @@ } | ||
session_token?: string; | ||
session_jwt?: string; | ||
session?: Session; | ||
@@ -28,0 +30,0 @@ } |
@@ -20,1 +20,6 @@ import type { AxiosRequestConfig } from "axios"; | ||
} | ||
export declare class ClientError extends Error { | ||
code: string; | ||
cause: unknown; | ||
constructor(code: string, message: string, cause?: unknown); | ||
} |
@@ -57,2 +57,3 @@ import { Session } from "./shared"; | ||
session_token?: string; | ||
session_jwt?: string; | ||
session_duration_minutes?: number; | ||
@@ -64,2 +65,3 @@ } | ||
session_token?: string; | ||
session_jwt?: string; | ||
session?: Session; | ||
@@ -66,0 +68,0 @@ } |
@@ -6,2 +6,3 @@ import type { AxiosInstance } from "axios"; | ||
session_token?: string; | ||
session_jwt?: string; | ||
session_duration_minutes?: number; | ||
@@ -17,2 +18,3 @@ } | ||
session_token: string; | ||
session_jwt: string; | ||
}; | ||
@@ -19,0 +21,0 @@ } |
@@ -72,2 +72,3 @@ import type { AxiosInstance } from "axios"; | ||
session_token?: string; | ||
session_jwt?: string; | ||
session_duration_minutes?: number; | ||
@@ -79,2 +80,3 @@ } | ||
session_token?: string; | ||
session_jwt?: string; | ||
session?: Session; | ||
@@ -81,0 +83,0 @@ } |
@@ -0,1 +1,2 @@ | ||
import * as jose from "jose"; | ||
import { Session } from "./shared"; | ||
@@ -10,5 +11,20 @@ import type { AxiosInstance } from "axios"; | ||
} | ||
export interface JwksResponse extends BaseResponse { | ||
keys: JWK[]; | ||
} | ||
export interface JWK { | ||
alg: string; | ||
key_ops: string[]; | ||
kid: string; | ||
kty: string; | ||
use: string; | ||
x5c: string[]; | ||
"x5t#S256": string; | ||
n: string; | ||
e: string; | ||
} | ||
export interface AuthenticateRequest { | ||
session_token: string; | ||
session_duration_minutes?: number; | ||
session_token?: string; | ||
session_jwt?: string; | ||
} | ||
@@ -18,2 +34,3 @@ export interface AuthenticateResponse extends BaseResponse { | ||
session_token: string; | ||
session_jwt: string; | ||
} | ||
@@ -23,12 +40,54 @@ export interface RevokeRequest { | ||
session_token?: string; | ||
session_jwt?: string; | ||
} | ||
export declare type RevokeResponse = BaseResponse; | ||
interface JwtConfig { | ||
projectID: string; | ||
jwks: jose.JWTVerifyGetKey; | ||
} | ||
export declare class Sessions { | ||
base_path: string; | ||
private client; | ||
constructor(client: AxiosInstance); | ||
private jwksClient; | ||
private jwtOptions; | ||
constructor(client: AxiosInstance, jwtConfig: JwtConfig); | ||
private endpoint; | ||
get(params: GetRequest): Promise<GetResponse>; | ||
jwks(project_id: string): Promise<JwksResponse>; | ||
authenticate(data: AuthenticateRequest): Promise<AuthenticateResponse>; | ||
/** Parse a JWT and verify the signature, preferring local verification over remote. | ||
* | ||
* If max_token_age_seconds is set, remote verification will be forced if the JWT was issued at | ||
* (based on the "iat" claim) more than that many seconds ago. | ||
* | ||
* To force remote validation for all tokens, set max_token_age_seconds to zero or use the | ||
* authenticate method instead. | ||
*/ | ||
authenticateJwt(jwt: string, options?: { | ||
max_token_age_seconds?: number; | ||
}): Promise<{ | ||
session: Session; | ||
session_jwt: string; | ||
}>; | ||
/** Parse a JWT and verify the signature locally (without calling /authenticate in the API). | ||
* | ||
* If maxTokenAge is set, this will return an error if the JWT was issued (based on the "iat" | ||
* claim) more than maxTokenAge seconds ago. | ||
* | ||
* If max_token_age_seconds is explicitly set to zero, all tokens will be considered too old, | ||
* even if they are otherwise valid. | ||
* | ||
* The value for current_date is used to compare timestamp claims ("exp", "nbf", "iat"). It | ||
* defaults to the current date (new Date()). | ||
* | ||
* The value for clock_tolerance_seconds is the maximum allowable difference when comparing | ||
* timestamps. It defaults to zero. | ||
*/ | ||
authenticateJwtLocal(jwt: string, options?: { | ||
clock_tolerance_seconds?: number; | ||
max_token_age_seconds?: number; | ||
current_date?: Date; | ||
}): Promise<Session>; | ||
revoke(data: RevokeRequest): Promise<RevokeResponse>; | ||
} | ||
export {}; |
@@ -22,2 +22,3 @@ import type { AxiosInstance } from "axios"; | ||
session_token?: string; | ||
session_jwt?: string; | ||
session_duration_minutes?: number; | ||
@@ -29,2 +30,3 @@ } | ||
session_token?: string; | ||
session_jwt?: string; | ||
session?: Session; | ||
@@ -43,2 +45,3 @@ } | ||
session_token?: string; | ||
session_jwt?: string; | ||
session_duration_minutes?: number; | ||
@@ -50,2 +53,3 @@ } | ||
session_token?: string; | ||
session_jwt?: string; | ||
session?: Session; | ||
@@ -52,0 +56,0 @@ } |
@@ -34,2 +34,3 @@ import { Session } from "./shared"; | ||
session_token?: string; | ||
session_jwt?: string; | ||
session_duration_minutes?: number; | ||
@@ -41,2 +42,3 @@ } | ||
session_token?: string; | ||
session_jwt?: string; | ||
session?: Session; | ||
@@ -43,0 +45,0 @@ } |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
65190
1780
2
+ Addedjose@^4.6.0
+ Addedjose@4.15.9(transitive)