@oneunit/auth 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +465 -0
- package/CHANGELOG.md +214 -0
- package/LICENSE +21 -0
- package/README.md +647 -0
- package/dist/adapters.d.ts +51 -0
- package/dist/adapters.d.ts.map +1 -0
- package/dist/adapters.js +301 -0
- package/dist/adapters.js.map +1 -0
- package/dist/auth.d.ts +59 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +560 -0
- package/dist/auth.js.map +1 -0
- package/dist/errors.d.ts +39 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +65 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/jwt.d.ts +11 -0
- package/dist/jwt.d.ts.map +1 -0
- package/dist/jwt.js +125 -0
- package/dist/jwt.js.map +1 -0
- package/dist/oauth.d.ts +31 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +178 -0
- package/dist/oauth.js.map +1 -0
- package/dist/password.d.ts +5 -0
- package/dist/password.d.ts.map +1 -0
- package/dist/password.js +90 -0
- package/dist/password.js.map +1 -0
- package/dist/providers.d.ts +37 -0
- package/dist/providers.d.ts.map +1 -0
- package/dist/providers.js +471 -0
- package/dist/providers.js.map +1 -0
- package/dist/rbac.d.ts +40 -0
- package/dist/rbac.d.ts.map +1 -0
- package/dist/rbac.js +240 -0
- package/dist/rbac.js.map +1 -0
- package/dist/roles.d.ts +2 -0
- package/dist/roles.d.ts.map +1 -0
- package/dist/roles.js +2 -0
- package/dist/roles.js.map +1 -0
- package/dist/token.d.ts +2 -0
- package/dist/token.d.ts.map +1 -0
- package/dist/token.js +2 -0
- package/dist/token.js.map +1 -0
- package/dist/types.d.ts +383 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +35 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +192 -0
- package/dist/utils.js.map +1 -0
- package/examples/express.ts +111 -0
- package/examples/fastify.ts +59 -0
- package/examples/oauth-social.ts +83 -0
- package/examples/standalone.ts +67 -0
- package/examples/uwebsockets.ts +143 -0
- package/package.json +86 -0
- package/src/adapters.ts +333 -0
- package/src/auth.ts +684 -0
- package/src/errors.ts +76 -0
- package/src/index.ts +124 -0
- package/src/jwt.ts +159 -0
- package/src/oauth.ts +226 -0
- package/src/password.ts +111 -0
- package/src/providers.ts +551 -0
- package/src/rbac.ts +285 -0
- package/src/roles.ts +1 -0
- package/src/token.ts +1 -0
- package/src/types.ts +432 -0
- package/src/utils.ts +231 -0
package/dist/errors.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export class AuthError extends Error {
|
|
2
|
+
name;
|
|
3
|
+
code;
|
|
4
|
+
status;
|
|
5
|
+
cause;
|
|
6
|
+
constructor(message, { code = "AUTH_ERROR", status = 401, cause } = {}) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = this.constructor.name;
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.status = status;
|
|
11
|
+
if (cause !== undefined) {
|
|
12
|
+
this.cause = cause;
|
|
13
|
+
}
|
|
14
|
+
Error.captureStackTrace?.(this, this.constructor);
|
|
15
|
+
}
|
|
16
|
+
toJSON() {
|
|
17
|
+
return {
|
|
18
|
+
name: this.name,
|
|
19
|
+
message: this.message,
|
|
20
|
+
code: this.code,
|
|
21
|
+
status: this.status,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export class InvalidTokenError extends AuthError {
|
|
26
|
+
constructor(message = "Invalid or expired token", options = {}) {
|
|
27
|
+
super(message, { code: "INVALID_TOKEN", status: 401, ...options });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export class TokenExpiredError extends AuthError {
|
|
31
|
+
constructor(message = "Token has expired", options = {}) {
|
|
32
|
+
super(message, { code: "TOKEN_EXPIRED", status: 401, ...options });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export class UnauthorizedError extends AuthError {
|
|
36
|
+
constructor(message = "Unauthorized", options = {}) {
|
|
37
|
+
super(message, { code: "UNAUTHORIZED", status: 401, ...options });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export class ForbiddenError extends AuthError {
|
|
41
|
+
constructor(message = "Forbidden", options = {}) {
|
|
42
|
+
super(message, { code: "FORBIDDEN", status: 403, ...options });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export class ConfigurationError extends AuthError {
|
|
46
|
+
constructor(message = "Invalid authentication configuration", options = {}) {
|
|
47
|
+
super(message, { code: "CONFIGURATION_ERROR", status: 500, ...options });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export class OAuthError extends AuthError {
|
|
51
|
+
constructor(message = "OAuth authentication failed", options = {}) {
|
|
52
|
+
super(message, { code: "OAUTH_ERROR", status: 401, ...options });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export class ProviderError extends AuthError {
|
|
56
|
+
constructor(message = "Identity provider error", options = {}) {
|
|
57
|
+
super(message, { code: "PROVIDER_ERROR", status: 502, ...options });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export class ValidationError extends AuthError {
|
|
61
|
+
constructor(message = "Validation failed", options = {}) {
|
|
62
|
+
super(message, { code: "VALIDATION_ERROR", status: 400, ...options });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,SAAU,SAAQ,KAAK;IAChB,IAAI,CAAS;IACtB,IAAI,CAAS;IACb,MAAM,CAAS;IACN,KAAK,CAAW;IAElC,YAAY,OAAe,EAAE,EAAE,IAAI,GAAG,YAAY,EAAE,MAAM,GAAG,GAAG,EAAE,KAAK,KAAuB,EAAE;QAC9F,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;QAClC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACrB,CAAC;QACD,KAAK,CAAC,iBAAiB,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACpD,CAAC;IAED,MAAM;QACJ,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,IAAI,CAAC,MAAM;SACpB,CAAC;IACJ,CAAC;CACF;AAED,MAAM,OAAO,iBAAkB,SAAQ,SAAS;IAC9C,YAAY,OAAO,GAAG,0BAA0B,EAAE,UAA4B,EAAE;QAC9E,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IACrE,CAAC;CACF;AAED,MAAM,OAAO,iBAAkB,SAAQ,SAAS;IAC9C,YAAY,OAAO,GAAG,mBAAmB,EAAE,UAA4B,EAAE;QACvE,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IACrE,CAAC;CACF;AAED,MAAM,OAAO,iBAAkB,SAAQ,SAAS;IAC9C,YAAY,OAAO,GAAG,cAAc,EAAE,UAA4B,EAAE;QAClE,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IACpE,CAAC;CACF;AAED,MAAM,OAAO,cAAe,SAAQ,SAAS;IAC3C,YAAY,OAAO,GAAG,WAAW,EAAE,UAA4B,EAAE;QAC/D,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IACjE,CAAC;CACF;AAED,MAAM,OAAO,kBAAmB,SAAQ,SAAS;IAC/C,YAAY,OAAO,GAAG,sCAAsC,EAAE,UAA4B,EAAE;QAC1F,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IAC3E,CAAC;CACF;AAED,MAAM,OAAO,UAAW,SAAQ,SAAS;IACvC,YAAY,OAAO,GAAG,6BAA6B,EAAE,UAA4B,EAAE;QACjF,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IACnE,CAAC;CACF;AAED,MAAM,OAAO,aAAc,SAAQ,SAAS;IAC1C,YAAY,OAAO,GAAG,yBAAyB,EAAE,UAA4B,EAAE;QAC7E,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IACtE,CAAC;CACF;AAED,MAAM,OAAO,eAAgB,SAAQ,SAAS;IAC5C,YAAY,OAAO,GAAG,mBAAmB,EAAE,UAA4B,EAAE;QACvE,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IACxE,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { AuthError, InvalidTokenError, TokenExpiredError, UnauthorizedError, ForbiddenError, ConfigurationError, OAuthError, ProviderError, ValidationError, } from "./errors.js";
|
|
2
|
+
export { encode, decode, decodeUnsafe, encodeAccessToken, encodeRefreshToken, } from "./jwt.js";
|
|
3
|
+
export { RBAC, createRBAC, defineRoles, matchPermission, } from "./rbac.js";
|
|
4
|
+
export { hashPassword, verifyPassword, needsRehash, } from "./password.js";
|
|
5
|
+
export { OAuth, createOAuth, createMemoryStateStore, } from "./oauth.js";
|
|
6
|
+
export { createProvider, getProvider, builtinProviders, pkceVerifier, pkceChallenge, google, github, instagram, facebook, twitter, discord, apple, linkedin, microsoft, reddit, twitch, slack, spotify, tiktok, } from "./providers.js";
|
|
7
|
+
export { Auth, auth, createAuth, createMemoryRefreshStore, } from "./auth.js";
|
|
8
|
+
export { expressAdapter, fastifyAdapter, koaAdapter, uwsAdapter, snapshotUwsRequest, createAdapters, } from "./adapters.js";
|
|
9
|
+
export { parseExpiresIn, isValidExpiresIn, randomToken, randomState, extractBearerToken, } from "./utils.js";
|
|
10
|
+
export type { AuthErrorOptions, JwtSignOptions, JwtVerifyOptions, JwtPayload, Secret, RoleDefinition, RBACOptions, AuthSubject, PasswordOptions, OAuthProfile, OAuthTokens, OAuthProviderConfig, OAuthProvider, ProviderDefinition, StateStore, OAuthOptions, OAuthAuthorizeOptions, UserRecord, UserStore, RefreshRecord, RefreshStore, LoginOptions, LoginResult, AuthOptions, RequestLike, ExtractTokenOptions, UwsHttpResponse, UwsHttpRequest, UwsRequestSnapshot, UwsHandler, ExpressRequestLike, ExpressResponseLike, ExpressNext, KoaContextLike, FastifyLike, FastifyRequestLike, FastifyReplyLike, } from "./types.js";
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,MAAM,EACN,MAAM,EACN,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,IAAI,EACJ,UAAU,EACV,WAAW,EACX,eAAe,GAChB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,YAAY,EACZ,cAAc,EACd,WAAW,GACZ,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,KAAK,EACL,WAAW,EACX,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,cAAc,EACd,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,MAAM,EACN,MAAM,EACN,SAAS,EACT,QAAQ,EACR,OAAO,EACP,OAAO,EACP,KAAK,EACL,QAAQ,EACR,SAAS,EACT,MAAM,EACN,MAAM,EACN,KAAK,EACL,OAAO,EACP,MAAM,GACP,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,IAAI,EACJ,IAAI,EACJ,UAAU,EACV,wBAAwB,GACzB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,cAAc,EACd,cAAc,EACd,UAAU,EACV,UAAU,EACV,kBAAkB,EAClB,cAAc,GACf,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,WAAW,EACX,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB,YAAY,EACV,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,MAAM,EACN,cAAc,EACd,WAAW,EACX,WAAW,EACX,eAAe,EACf,YAAY,EACZ,WAAW,EACX,mBAAmB,EACnB,aAAa,EACb,kBAAkB,EAClB,UAAU,EACV,YAAY,EACZ,qBAAqB,EACrB,UAAU,EACV,SAAS,EACT,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,WAAW,EACX,WAAW,EACX,mBAAmB,EACnB,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,UAAU,EACV,kBAAkB,EAClB,mBAAmB,EACnB,WAAW,EACX,cAAc,EACd,WAAW,EACX,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { AuthError, InvalidTokenError, TokenExpiredError, UnauthorizedError, ForbiddenError, ConfigurationError, OAuthError, ProviderError, ValidationError, } from "./errors.js";
|
|
2
|
+
export { encode, decode, decodeUnsafe, encodeAccessToken, encodeRefreshToken, } from "./jwt.js";
|
|
3
|
+
export { RBAC, createRBAC, defineRoles, matchPermission, } from "./rbac.js";
|
|
4
|
+
export { hashPassword, verifyPassword, needsRehash, } from "./password.js";
|
|
5
|
+
export { OAuth, createOAuth, createMemoryStateStore, } from "./oauth.js";
|
|
6
|
+
export { createProvider, getProvider, builtinProviders, pkceVerifier, pkceChallenge, google, github, instagram, facebook, twitter, discord, apple, linkedin, microsoft, reddit, twitch, slack, spotify, tiktok, } from "./providers.js";
|
|
7
|
+
export { Auth, auth, createAuth, createMemoryRefreshStore, } from "./auth.js";
|
|
8
|
+
export { expressAdapter, fastifyAdapter, koaAdapter, uwsAdapter, snapshotUwsRequest, createAdapters, } from "./adapters.js";
|
|
9
|
+
export { parseExpiresIn, isValidExpiresIn, randomToken, randomState, extractBearerToken, } from "./utils.js";
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,MAAM,EACN,MAAM,EACN,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,IAAI,EACJ,UAAU,EACV,WAAW,EACX,eAAe,GAChB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,YAAY,EACZ,cAAc,EACd,WAAW,GACZ,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,KAAK,EACL,WAAW,EACX,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,cAAc,EACd,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,MAAM,EACN,MAAM,EACN,SAAS,EACT,QAAQ,EACR,OAAO,EACP,OAAO,EACP,KAAK,EACL,QAAQ,EACR,SAAS,EACT,MAAM,EACN,MAAM,EACN,KAAK,EACL,OAAO,EACP,MAAM,GACP,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,IAAI,EACJ,IAAI,EACJ,UAAU,EACV,wBAAwB,GACzB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,cAAc,EACd,cAAc,EACd,UAAU,EACV,UAAU,EACV,kBAAkB,EAClB,cAAc,GACf,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,WAAW,EACX,kBAAkB,GACnB,MAAM,YAAY,CAAC"}
|
package/dist/jwt.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import jwt from "jsonwebtoken";
|
|
2
|
+
import type { JwtPayload, JwtSignOptions, JwtVerifyOptions, Secret } from "./types.js";
|
|
3
|
+
export declare function encode(payload: object | undefined, secret: Secret, options?: JwtSignOptions): string;
|
|
4
|
+
export declare function decode(token: string, secret: Secret, options?: JwtVerifyOptions): JwtPayload;
|
|
5
|
+
export declare function decodeUnsafe(token: string, options?: {
|
|
6
|
+
complete?: boolean;
|
|
7
|
+
}): unknown;
|
|
8
|
+
export declare function encodeAccessToken(payload: object, secret: Secret, options?: JwtSignOptions): string;
|
|
9
|
+
export declare function encodeRefreshToken(payload: object, secret: Secret, options?: JwtSignOptions): string;
|
|
10
|
+
export { jwt };
|
|
11
|
+
//# sourceMappingURL=jwt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,cAAc,CAAC;AAG/B,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAqDvF,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,YAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,MAAM,CAwBjG;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,UAAU,CAmChG;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAKzF;AAkBD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,MAAM,CAMvG;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,MAAM,CAMxG;AAED,OAAO,EAAE,GAAG,EAAE,CAAC"}
|
package/dist/jwt.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import jwt from "jsonwebtoken";
|
|
2
|
+
import { AuthError, InvalidTokenError, TokenExpiredError, ValidationError } from "./errors.js";
|
|
3
|
+
import { isValidExpiresIn, parseExpiresIn } from "./utils.js";
|
|
4
|
+
const DEFAULT_ALGORITHM = "HS256";
|
|
5
|
+
const DEFAULT_EXPIRES_IN = 60 * 60 * 24;
|
|
6
|
+
function normalizeSecret(secret) {
|
|
7
|
+
if (typeof secret === "string" && secret.length > 0) {
|
|
8
|
+
return secret;
|
|
9
|
+
}
|
|
10
|
+
if (Buffer.isBuffer(secret) && secret.length > 0) {
|
|
11
|
+
return secret;
|
|
12
|
+
}
|
|
13
|
+
throw new ValidationError("A non-empty secret is required to sign or verify tokens");
|
|
14
|
+
}
|
|
15
|
+
function signOptions({ expiresIn, subject, audience, issuer, jwtid, notBefore, header, algorithm = DEFAULT_ALGORITHM, keyid, } = {}) {
|
|
16
|
+
const options = { algorithm: algorithm };
|
|
17
|
+
if (expiresIn !== undefined && expiresIn !== null) {
|
|
18
|
+
// Resolve the TTL here so an unparsable value fails loudly instead of being
|
|
19
|
+
// silently defaulted by parseExpiresIn while jsonwebtoken rejects it later.
|
|
20
|
+
if (!isValidExpiresIn(expiresIn)) {
|
|
21
|
+
throw new ValidationError(`Invalid expiresIn: ${JSON.stringify(expiresIn)}. Use seconds or a timespan like "15m", "7d".`);
|
|
22
|
+
}
|
|
23
|
+
options.expiresIn = (typeof expiresIn === "number"
|
|
24
|
+
? parseExpiresIn(expiresIn)
|
|
25
|
+
: expiresIn);
|
|
26
|
+
}
|
|
27
|
+
if (subject !== undefined)
|
|
28
|
+
options.subject = String(subject);
|
|
29
|
+
if (audience !== undefined)
|
|
30
|
+
options.audience = audience;
|
|
31
|
+
if (issuer !== undefined)
|
|
32
|
+
options.issuer = issuer;
|
|
33
|
+
if (jwtid !== undefined)
|
|
34
|
+
options.jwtid = jwtid;
|
|
35
|
+
if (notBefore !== undefined)
|
|
36
|
+
options.notBefore = notBefore;
|
|
37
|
+
if (keyid !== undefined)
|
|
38
|
+
options.keyid = keyid;
|
|
39
|
+
if (header !== undefined)
|
|
40
|
+
options.header = header;
|
|
41
|
+
return options;
|
|
42
|
+
}
|
|
43
|
+
export function encode(payload = {}, secret, options = {}) {
|
|
44
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
45
|
+
throw new ValidationError("Token payload must be an object");
|
|
46
|
+
}
|
|
47
|
+
const { expiresInSeconds, expiresIn = expiresInSeconds ?? DEFAULT_EXPIRES_IN, subject, ...rest } = options;
|
|
48
|
+
try {
|
|
49
|
+
const signPayload = { ...payload };
|
|
50
|
+
if (subject !== undefined && signPayload.sub === undefined) {
|
|
51
|
+
signPayload.sub = String(subject);
|
|
52
|
+
}
|
|
53
|
+
return jwt.sign(signPayload, normalizeSecret(secret), signOptions({ expiresIn, ...rest }));
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (error instanceof AuthError) {
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
throw new InvalidTokenError(error instanceof Error ? error.message : "Failed to sign token", { cause: error });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export function decode(token, secret, options = {}) {
|
|
63
|
+
if (!token || typeof token !== "string") {
|
|
64
|
+
throw new InvalidTokenError("Token is required");
|
|
65
|
+
}
|
|
66
|
+
const { algorithms = [DEFAULT_ALGORITHM], audience, issuer, subject, clockTolerance, ignoreExpiration = false, complete = false, } = options;
|
|
67
|
+
try {
|
|
68
|
+
const verifyOptions = {
|
|
69
|
+
algorithms: algorithms,
|
|
70
|
+
audience: audience,
|
|
71
|
+
issuer,
|
|
72
|
+
subject,
|
|
73
|
+
clockTolerance,
|
|
74
|
+
ignoreExpiration,
|
|
75
|
+
complete,
|
|
76
|
+
};
|
|
77
|
+
return jwt.verify(token, normalizeSecret(secret), verifyOptions);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
if (error instanceof AuthError) {
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
if (error instanceof Error && error.name === "TokenExpiredError") {
|
|
84
|
+
throw new TokenExpiredError("Token has expired", { cause: error });
|
|
85
|
+
}
|
|
86
|
+
throw new InvalidTokenError("Invalid or expired token", { cause: error });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export function decodeUnsafe(token, options = {}) {
|
|
90
|
+
if (!token || typeof token !== "string") {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
return jwt.decode(token, { complete: options.complete === true }) ?? null;
|
|
94
|
+
}
|
|
95
|
+
function withTyp(payload, typ, options = { secret: "" }) {
|
|
96
|
+
const next = { ...payload, typ };
|
|
97
|
+
const subject = options.subject ?? next.sub ?? next.userId;
|
|
98
|
+
if (subject !== undefined && next.sub === undefined) {
|
|
99
|
+
next.sub = String(subject);
|
|
100
|
+
}
|
|
101
|
+
return encode(next, options.secret, {
|
|
102
|
+
expiresIn: options.expiresIn,
|
|
103
|
+
audience: options.audience,
|
|
104
|
+
issuer: options.issuer,
|
|
105
|
+
jwtid: options.jwtid,
|
|
106
|
+
algorithm: options.algorithm,
|
|
107
|
+
header: options.header,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
export function encodeAccessToken(payload, secret, options = {}) {
|
|
111
|
+
return withTyp(payload, "access", {
|
|
112
|
+
...options,
|
|
113
|
+
secret,
|
|
114
|
+
expiresIn: options.expiresIn ?? "15m",
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
export function encodeRefreshToken(payload, secret, options = {}) {
|
|
118
|
+
return withTyp(payload, "refresh", {
|
|
119
|
+
...options,
|
|
120
|
+
secret,
|
|
121
|
+
expiresIn: options.expiresIn ?? "7d",
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
export { jwt };
|
|
125
|
+
//# sourceMappingURL=jwt.js.map
|
package/dist/jwt.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.js","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"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"}
|
package/dist/oauth.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { OAuthAuthorizeOptions, OAuthOptions, OAuthProfile, OAuthProvider, OAuthProviderConfig, OAuthStateRecord, OAuthTokens, StateStore } from "./types.js";
|
|
2
|
+
interface ProviderEntry {
|
|
3
|
+
provider: OAuthProvider;
|
|
4
|
+
config: OAuthProviderConfig;
|
|
5
|
+
}
|
|
6
|
+
export declare class OAuth {
|
|
7
|
+
providers: Map<string, ProviderEntry>;
|
|
8
|
+
defaultRedirectUri: string | null;
|
|
9
|
+
stateStore: StateStore;
|
|
10
|
+
constructor(options?: OAuthOptions);
|
|
11
|
+
use(id: string, config?: OAuthProviderConfig): this;
|
|
12
|
+
list(): string[];
|
|
13
|
+
get(id: string): ProviderEntry;
|
|
14
|
+
authorize(id: string, options?: OAuthAuthorizeOptions): Promise<{
|
|
15
|
+
url: string;
|
|
16
|
+
state: string;
|
|
17
|
+
provider: string;
|
|
18
|
+
codeVerifier?: string;
|
|
19
|
+
}>;
|
|
20
|
+
callback(id: string, params?: string | Record<string, string>): Promise<{
|
|
21
|
+
provider: string;
|
|
22
|
+
tokens: OAuthTokens;
|
|
23
|
+
profile: OAuthProfile;
|
|
24
|
+
state: OAuthStateRecord | null;
|
|
25
|
+
}>;
|
|
26
|
+
verifyState(state: string, expected?: string): Promise<boolean>;
|
|
27
|
+
}
|
|
28
|
+
export declare function createMemoryStateStore(): StateStore;
|
|
29
|
+
export declare function createOAuth(options?: OAuthOptions): OAuth;
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=oauth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,qBAAqB,EACrB,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,mBAAmB,EACnB,gBAAgB,EAChB,WAAW,EACX,UAAU,EACX,MAAM,YAAY,CAAC;AAGpB,UAAU,aAAa;IACrB,QAAQ,EAAE,aAAa,CAAC;IACxB,MAAM,EAAE,mBAAmB,CAAC;CAC7B;AAED,qBAAa,KAAK;IAChB,SAAS,6BAAoC;IAC7C,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,UAAU,EAAE,UAAU,CAAC;gBAEX,OAAO,GAAE,YAAiB;IAUtC,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,GAAE,mBAAwB,GAAG,IAAI;IAoCvD,IAAI,IAAI,MAAM,EAAE;IAIhB,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa;IAQxB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAAC;QACxE,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IA6BI,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAAG,OAAO,CAAC;QAChF,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,WAAW,CAAC;QACpB,OAAO,EAAE,YAAY,CAAC;QACtB,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAC;KAChC,CAAC;IA6CI,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAUtE;AAoBD,wBAAgB,sBAAsB,IAAI,UAAU,CAyBnD;AAED,wBAAgB,WAAW,CAAC,OAAO,GAAE,YAAiB,GAAG,KAAK,CAE7D"}
|
package/dist/oauth.js
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { OAuthError, ValidationError } from "./errors.js";
|
|
2
|
+
import { builtinProviders, createProvider, getProvider, pkceVerifier } from "./providers.js";
|
|
3
|
+
import { randomState, timingSafeEqualString } from "./utils.js";
|
|
4
|
+
export class OAuth {
|
|
5
|
+
providers = new Map();
|
|
6
|
+
defaultRedirectUri;
|
|
7
|
+
stateStore;
|
|
8
|
+
constructor(options = {}) {
|
|
9
|
+
this.defaultRedirectUri = options.redirectUri ?? null;
|
|
10
|
+
this.stateStore = options.stateStore ?? createMemoryStateStore();
|
|
11
|
+
const configured = options.providers ?? {};
|
|
12
|
+
for (const [id, config] of Object.entries(configured)) {
|
|
13
|
+
this.use(id, config);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
use(id, config = {}) {
|
|
17
|
+
const provider = config.provider ??
|
|
18
|
+
(id in builtinProviders
|
|
19
|
+
? getProvider(id)
|
|
20
|
+
: createProvider({
|
|
21
|
+
id,
|
|
22
|
+
authorizationUrl: String(config.authorizationUrl ?? ""),
|
|
23
|
+
tokenUrl: String(config.tokenUrl ?? ""),
|
|
24
|
+
...config,
|
|
25
|
+
}));
|
|
26
|
+
const entry = {
|
|
27
|
+
provider,
|
|
28
|
+
config: {
|
|
29
|
+
clientId: config.clientId,
|
|
30
|
+
clientSecret: config.clientSecret,
|
|
31
|
+
redirectUri: config.redirectUri ?? this.defaultRedirectUri ?? undefined,
|
|
32
|
+
scopes: config.scopes,
|
|
33
|
+
pkce: config.pkce,
|
|
34
|
+
extraAuthParams: config.extraAuthParams,
|
|
35
|
+
extraTokenParams: config.extraTokenParams,
|
|
36
|
+
userInfoHeaders: config.userInfoHeaders,
|
|
37
|
+
tokenAuthMethod: config.tokenAuthMethod,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
this.providers.set(id, entry);
|
|
41
|
+
// Also expose the provider under its own id when registered via an alias,
|
|
42
|
+
// but never overwrite an entry that was configured explicitly.
|
|
43
|
+
if (provider.id && provider.id !== id && !this.providers.has(provider.id)) {
|
|
44
|
+
this.providers.set(provider.id, entry);
|
|
45
|
+
}
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
list() {
|
|
49
|
+
return [...new Set([...this.providers.values()].map((entry) => entry.provider.id))];
|
|
50
|
+
}
|
|
51
|
+
get(id) {
|
|
52
|
+
const entry = this.providers.get(id);
|
|
53
|
+
if (!entry) {
|
|
54
|
+
throw new ValidationError(`OAuth provider "${id}" is not configured`);
|
|
55
|
+
}
|
|
56
|
+
return entry;
|
|
57
|
+
}
|
|
58
|
+
async authorize(id, options = {}) {
|
|
59
|
+
const { provider, config } = this.get(id);
|
|
60
|
+
const state = options.state ?? randomState();
|
|
61
|
+
const redirectUri = options.redirectUri ?? config.redirectUri;
|
|
62
|
+
const codeVerifier = provider.pkce || config.pkce ? (options.codeVerifier ?? pkceVerifier()) : undefined;
|
|
63
|
+
const authorization = provider.createAuthorizationUrl(config, {
|
|
64
|
+
state,
|
|
65
|
+
redirectUri,
|
|
66
|
+
codeVerifier,
|
|
67
|
+
extraParams: options.extraParams,
|
|
68
|
+
});
|
|
69
|
+
await this.stateStore.set(state, {
|
|
70
|
+
provider: id,
|
|
71
|
+
redirectUri,
|
|
72
|
+
codeVerifier: authorization.codeVerifier ?? codeVerifier,
|
|
73
|
+
createdAt: Date.now(),
|
|
74
|
+
metadata: options.metadata ?? {},
|
|
75
|
+
}, options.ttlSeconds ?? 600);
|
|
76
|
+
return {
|
|
77
|
+
url: authorization.url,
|
|
78
|
+
state,
|
|
79
|
+
provider: id,
|
|
80
|
+
codeVerifier: authorization.codeVerifier ?? codeVerifier,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
async callback(id, params = {}) {
|
|
84
|
+
const query = normalizeCallbackParams(params);
|
|
85
|
+
if (query.error) {
|
|
86
|
+
throw new OAuthError(query.error_description || query.error);
|
|
87
|
+
}
|
|
88
|
+
if (!query.code) {
|
|
89
|
+
throw new ValidationError("OAuth callback is missing authorization code");
|
|
90
|
+
}
|
|
91
|
+
const { provider, config } = this.get(id);
|
|
92
|
+
let stored = null;
|
|
93
|
+
if (query.state) {
|
|
94
|
+
stored = (await this.stateStore.get(query.state));
|
|
95
|
+
if (!stored) {
|
|
96
|
+
throw new OAuthError("Invalid or expired OAuth state");
|
|
97
|
+
}
|
|
98
|
+
if (stored.provider && stored.provider !== id) {
|
|
99
|
+
throw new OAuthError("OAuth provider mismatch");
|
|
100
|
+
}
|
|
101
|
+
await this.stateStore.delete(query.state);
|
|
102
|
+
}
|
|
103
|
+
else if (config.pkce || provider.pkce) {
|
|
104
|
+
throw new OAuthError("OAuth state is required");
|
|
105
|
+
}
|
|
106
|
+
const tokens = await provider.exchangeCode(config, {
|
|
107
|
+
code: query.code,
|
|
108
|
+
redirectUri: stored?.redirectUri ?? config.redirectUri,
|
|
109
|
+
codeVerifier: stored?.codeVerifier ?? query.codeVerifier,
|
|
110
|
+
});
|
|
111
|
+
const profile = await provider.fetchProfile(config, tokens);
|
|
112
|
+
return {
|
|
113
|
+
provider: id,
|
|
114
|
+
tokens,
|
|
115
|
+
profile: {
|
|
116
|
+
...profile,
|
|
117
|
+
provider: profile.provider ?? id,
|
|
118
|
+
},
|
|
119
|
+
state: stored,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
async verifyState(state, expected) {
|
|
123
|
+
const stored = await this.stateStore.get(state);
|
|
124
|
+
if (!stored) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
if (expected && !timingSafeEqualString(stored.provider, expected)) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function normalizeCallbackParams(params) {
|
|
134
|
+
if (typeof params === "string") {
|
|
135
|
+
const trimmed = params.trim();
|
|
136
|
+
// Accepts a full URL ("https://host/cb?code=..."), a path with a query
|
|
137
|
+
// ("/cb?code=..." or "cb?code=..."), a leading "?code=...", or a bare query
|
|
138
|
+
// string ("code=..."). Only the bare form needs special handling: the URL
|
|
139
|
+
// constructor reads it as a path, and reading it as a query string is the
|
|
140
|
+
// only way to find the code.
|
|
141
|
+
const isBareQuery = trimmed !== "" && !trimmed.includes("?") && !trimmed.includes("://") && !trimmed.startsWith("/");
|
|
142
|
+
if (isBareQuery) {
|
|
143
|
+
return Object.fromEntries(new URLSearchParams(trimmed));
|
|
144
|
+
}
|
|
145
|
+
const url = trimmed.includes("://") ? new URL(trimmed) : new URL(trimmed, "http://localhost");
|
|
146
|
+
return Object.fromEntries(url.searchParams.entries());
|
|
147
|
+
}
|
|
148
|
+
return params ?? {};
|
|
149
|
+
}
|
|
150
|
+
export function createMemoryStateStore() {
|
|
151
|
+
const store = new Map();
|
|
152
|
+
return {
|
|
153
|
+
async set(key, value, ttlSeconds = 600) {
|
|
154
|
+
store.set(key, {
|
|
155
|
+
value,
|
|
156
|
+
expiresAt: Date.now() + ttlSeconds * 1000,
|
|
157
|
+
});
|
|
158
|
+
},
|
|
159
|
+
async get(key) {
|
|
160
|
+
const entry = store.get(key);
|
|
161
|
+
if (!entry) {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
if (entry.expiresAt <= Date.now()) {
|
|
165
|
+
store.delete(key);
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
return entry.value;
|
|
169
|
+
},
|
|
170
|
+
async delete(key) {
|
|
171
|
+
store.delete(key);
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
export function createOAuth(options = {}) {
|
|
176
|
+
return new OAuth(options);
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { PasswordOptions } from "./types.js";
|
|
2
|
+
export declare function hashPassword(password: string, options?: PasswordOptions): Promise<string>;
|
|
3
|
+
export declare function verifyPassword(password: string, storedHash: string): Promise<boolean>;
|
|
4
|
+
export declare function needsRehash(storedHash: string, options?: PasswordOptions): boolean;
|
|
5
|
+
//# sourceMappingURL=password.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"password.d.ts","sourceRoot":"","sources":["../src/password.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AA8BlD,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAsBnG;AAED,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CA+B3F;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAqBtF"}
|
package/dist/password.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { randomBytes, scrypt as scryptCallback, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { ValidationError } from "./errors.js";
|
|
3
|
+
function scrypt(password, salt, keyLength, options) {
|
|
4
|
+
return new Promise((resolve, reject) => {
|
|
5
|
+
scryptCallback(password, salt, keyLength, options, (error, derivedKey) => {
|
|
6
|
+
if (error) {
|
|
7
|
+
reject(error);
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
resolve(derivedKey);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
const DEFAULT_KEY_LENGTH = 64;
|
|
15
|
+
const DEFAULT_SALT_BYTES = 16;
|
|
16
|
+
const DEFAULT_COST = 16384;
|
|
17
|
+
const DEFAULT_BLOCK_SIZE = 8;
|
|
18
|
+
const DEFAULT_PARALLELISM = 1;
|
|
19
|
+
const PREFIX = "scrypt";
|
|
20
|
+
function toBuffer(value) {
|
|
21
|
+
return Buffer.isBuffer(value) ? value : Buffer.from(String(value), "utf8");
|
|
22
|
+
}
|
|
23
|
+
export async function hashPassword(password, options = {}) {
|
|
24
|
+
if (typeof password !== "string" || password.length === 0) {
|
|
25
|
+
throw new ValidationError("Password must be a non-empty string");
|
|
26
|
+
}
|
|
27
|
+
const saltBytes = options.saltBytes ?? DEFAULT_SALT_BYTES;
|
|
28
|
+
const keyLength = options.keyLength ?? DEFAULT_KEY_LENGTH;
|
|
29
|
+
const N = options.cost ?? DEFAULT_COST;
|
|
30
|
+
const r = options.blockSize ?? DEFAULT_BLOCK_SIZE;
|
|
31
|
+
const p = options.parallelism ?? DEFAULT_PARALLELISM;
|
|
32
|
+
const salt = options.salt ? toBuffer(options.salt) : randomBytes(saltBytes);
|
|
33
|
+
const key = await scrypt(password, salt, keyLength, { N, r, p });
|
|
34
|
+
return [
|
|
35
|
+
PREFIX,
|
|
36
|
+
N,
|
|
37
|
+
r,
|
|
38
|
+
p,
|
|
39
|
+
keyLength,
|
|
40
|
+
salt.toString("base64url"),
|
|
41
|
+
key.toString("base64url"),
|
|
42
|
+
].join("$");
|
|
43
|
+
}
|
|
44
|
+
export async function verifyPassword(password, storedHash) {
|
|
45
|
+
if (typeof password !== "string" || typeof storedHash !== "string") {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const parts = storedHash.split("$");
|
|
49
|
+
if (parts.length !== 7 || parts[0] !== PREFIX) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const [, nRaw, rRaw, pRaw, lengthRaw, saltB64, hashB64] = parts;
|
|
53
|
+
const N = Number.parseInt(nRaw, 10);
|
|
54
|
+
const r = Number.parseInt(rRaw, 10);
|
|
55
|
+
const p = Number.parseInt(pRaw, 10);
|
|
56
|
+
const keyLength = Number.parseInt(lengthRaw, 10);
|
|
57
|
+
if (![N, r, p, keyLength].every(Number.isFinite)) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const salt = Buffer.from(saltB64, "base64url");
|
|
62
|
+
const expected = Buffer.from(hashB64, "base64url");
|
|
63
|
+
const actual = await scrypt(password, salt, keyLength, { N, r, p });
|
|
64
|
+
if (actual.length !== expected.length) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
return timingSafeEqual(actual, expected);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export function needsRehash(storedHash, options = {}) {
|
|
74
|
+
if (typeof storedHash !== "string") {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
const parts = storedHash.split("$");
|
|
78
|
+
if (parts.length !== 7 || parts[0] !== PREFIX) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
const N = Number.parseInt(parts[1], 10);
|
|
82
|
+
const r = Number.parseInt(parts[2], 10);
|
|
83
|
+
const p = Number.parseInt(parts[3], 10);
|
|
84
|
+
const keyLength = Number.parseInt(parts[4], 10);
|
|
85
|
+
return (N !== (options.cost ?? DEFAULT_COST) ||
|
|
86
|
+
r !== (options.blockSize ?? DEFAULT_BLOCK_SIZE) ||
|
|
87
|
+
p !== (options.parallelism ?? DEFAULT_PARALLELISM) ||
|
|
88
|
+
keyLength !== (options.keyLength ?? DEFAULT_KEY_LENGTH));
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=password.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"password.js","sourceRoot":"","sources":["../src/password.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { OAuthProvider, ProviderDefinition } from "./types.js";
|
|
2
|
+
export declare function pkceVerifier(): string;
|
|
3
|
+
export declare function pkceChallenge(verifier: string): string;
|
|
4
|
+
export declare function createProvider(definition: ProviderDefinition): OAuthProvider;
|
|
5
|
+
export declare const google: OAuthProvider;
|
|
6
|
+
export declare const github: OAuthProvider;
|
|
7
|
+
export declare const instagram: OAuthProvider;
|
|
8
|
+
export declare const facebook: OAuthProvider;
|
|
9
|
+
export declare const twitter: OAuthProvider;
|
|
10
|
+
export declare const discord: OAuthProvider;
|
|
11
|
+
export declare const apple: OAuthProvider;
|
|
12
|
+
export declare const linkedin: OAuthProvider;
|
|
13
|
+
export declare const microsoft: OAuthProvider;
|
|
14
|
+
export declare const reddit: OAuthProvider;
|
|
15
|
+
export declare const twitch: OAuthProvider;
|
|
16
|
+
export declare const slack: OAuthProvider;
|
|
17
|
+
export declare const spotify: OAuthProvider;
|
|
18
|
+
export declare const tiktok: OAuthProvider;
|
|
19
|
+
export declare const builtinProviders: Readonly<{
|
|
20
|
+
google: OAuthProvider;
|
|
21
|
+
github: OAuthProvider;
|
|
22
|
+
instagram: OAuthProvider;
|
|
23
|
+
facebook: OAuthProvider;
|
|
24
|
+
twitter: OAuthProvider;
|
|
25
|
+
x: OAuthProvider;
|
|
26
|
+
discord: OAuthProvider;
|
|
27
|
+
apple: OAuthProvider;
|
|
28
|
+
linkedin: OAuthProvider;
|
|
29
|
+
microsoft: OAuthProvider;
|
|
30
|
+
reddit: OAuthProvider;
|
|
31
|
+
twitch: OAuthProvider;
|
|
32
|
+
slack: OAuthProvider;
|
|
33
|
+
spotify: OAuthProvider;
|
|
34
|
+
tiktok: OAuthProvider;
|
|
35
|
+
}>;
|
|
36
|
+
export declare function getProvider(id: string): OAuthProvider;
|
|
37
|
+
//# sourceMappingURL=providers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../src/providers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAKV,aAAa,EAIb,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAGpB,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEtD;AA2CD,wBAAgB,cAAc,CAAC,UAAU,EAAE,kBAAkB,GAAG,aAAa,CAoJ5E;AAED,eAAO,MAAM,MAAM,eAkBjB,CAAC;AAEH,eAAO,MAAM,MAAM,eAgDjB,CAAC;AAEH,eAAO,MAAM,SAAS,eAapB,CAAC;AAEH,eAAO,MAAM,QAAQ,eAmBnB,CAAC;AAEH,eAAO,MAAM,OAAO,eAgBlB,CAAC;AAEH,eAAO,MAAM,OAAO,eAkBlB,CAAC;AAIH,eAAO,MAAM,KAAK,eA4ChB,CAAC;AAEH,eAAO,MAAM,QAAQ,eAenB,CAAC;AAEH,eAAO,MAAM,SAAS,eAepB,CAAC;AAEH,eAAO,MAAM,MAAM,eAejB,CAAC;AAEH,eAAO,MAAM,MAAM,eAcjB,CAAC;AAEH,eAAO,MAAM,KAAK,eAahB,CAAC;AAEH,eAAO,MAAM,OAAO,eAqBlB,CAAC;AAEH,eAAO,MAAM,MAAM,eAajB,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;EAgBa,CAAC;AAE3C,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,CAMrD"}
|