New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

lucia

Package Overview
Dependencies
Maintainers
1
Versions
109
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

lucia - npm Package Compare versions

Comparing version 2.7.3 to 3.0.0-beta.0

dist/scrypt/index.d.ts

38

dist/auth/database.d.ts

@@ -1,15 +0,27 @@

export type KeySchema = {
import type { DatabaseSessionAttributes, DatabaseUserAttributes } from "../index.js";
export interface Adapter {
getSessionAndUser(sessionId: string): Promise<[session: DatabaseSession | null, user: DatabaseUser | null]>;
getUserSessions(userId: string): Promise<DatabaseSession[]>;
setSession(value: DatabaseSession): Promise<void>;
updateSession(sessionId: string, value: Partial<DatabaseSession>): Promise<void>;
deleteSession(sessionId: string): Promise<void>;
deleteUserSessions(userId: string): Promise<void>;
}
export interface SessionAdapter {
getSession(sessionId: string): Promise<DatabaseSession | null>;
getUserSessions(userId: string): Promise<DatabaseSession[]>;
setSession(value: DatabaseSession): Promise<void>;
updateSession(sessionId: string, value: Partial<DatabaseSession>): Promise<void>;
deleteSession(sessionId: string): Promise<void>;
deleteUserSessions(userId: string): Promise<void>;
}
export interface DatabaseUser {
id: string;
hashed_password: string | null;
user_id: string;
};
export type UserSchema = {
attributes: DatabaseUserAttributes;
}
export interface DatabaseSession {
sessionId: string;
expiresAt: Date;
id: string;
} & Lucia.DatabaseUserAttributes;
export type SessionSchema = {
id: string;
active_expires: number;
idle_expires: number;
user_id: string;
} & Lucia.DatabaseSessionAttributes;
export declare const createKeyId: (providerId: string, providerUserId: string) => string;
attributes: DatabaseSessionAttributes;
}

@@ -1,6 +0,1 @@

export const createKeyId = (providerId, providerUserId) => {
if (providerId.includes(":")) {
throw new TypeError("Provider id must not include any colons (:)");
}
return `${providerId}:${providerUserId}`;
};
export {};
import { AuthRequest } from "./request.js";
import { lucia as defaultMiddleware } from "../middleware/index.js";
import type { Cookie, SessionCookieConfiguration } from "./cookie.js";
import type { UserSchema, SessionSchema, KeySchema } from "./database.js";
import type { Adapter, SessionAdapter, InitializeAdapter } from "./adapter.js";
import type { Middleware } from "./request.js";
export type Session = Readonly<{
user: User;
sessionId: string;
activePeriodExpiresAt: Date;
idlePeriodExpiresAt: Date;
state: "idle" | "active";
import { TimeSpan } from "oslo";
import type { SessionCookie } from "oslo/session";
import type { Adapter } from "./database.js";
import type { DatabaseSessionAttributes, DatabaseUserAttributes, RegisteredLucia } from "../index.js";
type SessionAttributes = RegisteredLucia extends Lucia<any, infer _SessionAttributes> ? _SessionAttributes : {};
type UserAttributes = RegisteredLucia extends Lucia<any, any, infer _UserAttributes> ? _UserAttributes : {};
export interface Session extends SessionAttributes {
id: string;
expiresAt: Date;
fresh: boolean;
}> & ReturnType<Lucia.Auth["getSessionAttributes"]>;
export type Key = Readonly<{
userId: string;
providerId: string;
providerUserId: string;
passwordDefined: boolean;
}>;
export type Env = "DEV" | "PROD";
export type User = {
userId: string;
} & ReturnType<Lucia.Auth["getUserAttributes"]>;
export declare const lucia: <_Configuration extends Configuration<{}, {}>>(config: _Configuration) => Auth<_Configuration>;
export declare class Auth<_Configuration extends Configuration = any> {
}
export interface User extends UserAttributes {
id: string;
}
export declare class Lucia<_Middleware extends Middleware = Middleware<[RequestContext]>, _SessionAttributes extends {} = Record<never, never>, _UserAttributes extends {} = Record<never, never>> {
private adapter;
private sessionCookieConfig;
private sessionExpiresIn;
private sessionController;
private sessionCookieController;
private csrfProtection;
private env;
private passwordHash;
protected middleware: _Configuration["middleware"] extends Middleware ? _Configuration["middleware"] : ReturnType<typeof defaultMiddleware>;
private middleware;
private experimental;
constructor(config: _Configuration);
protected getUserAttributes: (databaseUser: UserSchema) => _Configuration extends Configuration<infer _UserAttributes> ? _UserAttributes : never;
protected getSessionAttributes: (databaseSession: SessionSchema) => _Configuration extends Configuration<any, infer _SessionAttributes> ? _SessionAttributes : never;
transformDatabaseUser: (databaseUser: UserSchema) => User;
transformDatabaseKey: (databaseKey: KeySchema) => Key;
transformDatabaseSession: (databaseSession: SessionSchema, context: {
private getSessionAttributes;
private getUserAttributes;
constructor(adapter: Adapter, options?: {
middleware?: _Middleware;
csrfProtection?: boolean | CSRFProtectionOptions;
sessionExpiresIn?: TimeSpan;
sessionCookie?: SessionCookieOptions;
getSessionAttributes?: (databaseSessionAttributes: DatabaseSessionAttributes) => _SessionAttributes;
getUserAttributes?: (databaseUserAttributes: DatabaseUserAttributes) => _UserAttributes;
experimental?: ExperimentalOptions;
});
getUserSessions(userId: string): Promise<Session[]>;
validateSession(sessionId: string): Promise<{
user: User;
fresh: boolean;
}) => Session;
private getDatabaseUser;
private getDatabaseSession;
private getDatabaseSessionAndUser;
private validateSessionIdArgument;
private getNewSessionExpiration;
getUser: (userId: string) => Promise<User>;
createUser: (options: {
userId?: string;
key: {
providerId: string;
providerUserId: string;
password: string | null;
} | null;
attributes: Lucia.DatabaseUserAttributes;
}) => Promise<User>;
updateUserAttributes: (userId: string, attributes: Partial<Lucia.DatabaseUserAttributes>) => Promise<User>;
deleteUser: (userId: string) => Promise<void>;
useKey: (providerId: string, providerUserId: string, password: string | null) => Promise<Key>;
getSession: (sessionId: string) => Promise<Session>;
getAllUserSessions: (userId: string) => Promise<Session[]>;
validateSession: (sessionId: string) => Promise<Session>;
createSession: (options: {
sessionId?: string;
userId: string;
attributes: Lucia.DatabaseSessionAttributes;
}) => Promise<Session>;
updateSessionAttributes: (sessionId: string, attributes: Partial<Lucia.DatabaseSessionAttributes>) => Promise<Session>;
invalidateSession: (sessionId: string) => Promise<void>;
invalidateAllUserSessions: (userId: string) => Promise<void>;
deleteDeadUserSessions: (userId: string) => Promise<void>;
/**
* @deprecated To be removed in next major release
*/
validateRequestOrigin: (request: {
url: string | null;
method: string | null;
headers: {
origin: string | null;
};
}) => void;
readSessionCookie: (cookieHeader: string | null | undefined) => string | null;
readBearerToken: (authorizationHeader: string | null | undefined) => string | null;
handleRequest: (...args: (_Configuration["middleware"] extends Middleware ? _Configuration["middleware"] : Middleware<[import("./request.js").RequestContext]>) extends Middleware<infer Args extends any[]> ? Args : never) => AuthRequest<Lucia.Auth>;
createSessionCookie: (session: Session | null) => Cookie;
createKey: (options: {
userId: string;
providerId: string;
providerUserId: string;
password: string | null;
}) => Promise<Key>;
deleteKey: (providerId: string, providerUserId: string) => Promise<void>;
getKey: (providerId: string, providerUserId: string) => Promise<Key>;
getAllUserKeys: (userId: string) => Promise<Key[]>;
updateKeyPassword: (providerId: string, providerUserId: string, password: string | null) => Promise<Key>;
session: Session;
} | {
user: null;
session: null;
}>;
createSession(userId: string, attributes: DatabaseSessionAttributes): Promise<Session>;
invalidateSession(sessionId: string): Promise<void>;
invalidateUserSessions(userId: string): Promise<void>;
readSessionCookie(cookieHeader: string): string | null;
readBearerToken(authorizationHeader: string): string | null;
handleRequest(...args: _Middleware extends Middleware<infer _Args> ? _Args : []): AuthRequest<typeof this>;
private verifyRequestOrigin;
createSessionCookie(sessionId: string): SessionCookie;
createBlankSessionCookie(): SessionCookie;
}
type MaybePromise<T> = T | Promise<T>;
export type Configuration<_UserAttributes extends Record<string, any> = {}, _SessionAttributes extends Record<string, any> = {}> = {
adapter: InitializeAdapter<Adapter> | {
user: InitializeAdapter<Adapter>;
session: InitializeAdapter<SessionAdapter>;
};
env: Env;
middleware?: Middleware;
csrfProtection?: boolean | {
host?: string;
hostHeader?: string;
allowedSubDomains?: string[] | "*";
};
sessionExpiresIn?: {
activePeriod: number;
idlePeriod: number;
};
sessionCookie?: SessionCookieConfiguration;
getSessionAttributes?: (databaseSession: SessionSchema) => _SessionAttributes;
getUserAttributes?: (databaseUser: UserSchema) => _UserAttributes;
passwordHash?: {
generate: (password: string) => MaybePromise<string>;
validate: (password: string, hash: string) => MaybePromise<boolean>;
};
experimental?: {
debugMode?: boolean;
};
};
export interface SessionCookieOptions {
name?: string;
expires?: boolean;
sameSite?: "lax" | "strict";
domain?: string;
path?: string;
secure?: boolean;
}
export interface CSRFProtectionOptions {
allowedDomains?: string[];
hostHeader?: string;
}
export interface ExperimentalOptions {
debugMode?: boolean;
}
export interface LuciaRequest {
method: string;
url?: string;
headers: Pick<Headers, "get">;
}
export interface RequestContext {
sessionCookie?: string | null;
request: LuciaRequest;
setCookie: (cookie: SessionCookie) => void;
}
export type Middleware<Args extends any[] = any> = (context: {
args: Args;
sessionCookieName: string;
}) => RequestContext;
export {};

@@ -1,365 +0,135 @@

import { DEFAULT_SESSION_COOKIE_NAME, createSessionCookie } from "./cookie.js";
import { logError } from "../utils/log.js";
import { generateScryptHash, validateScryptHash } from "../utils/crypto.js";
import { generateRandomString } from "../utils/crypto.js";
import { LuciaError } from "./error.js";
import { parseCookie } from "../utils/cookie.js";
import { isValidDatabaseSession } from "./session.js";
import { AuthRequest, transformRequestContext } from "./request.js";
import { AuthRequest } from "./request.js";
import { lucia as defaultMiddleware } from "../middleware/index.js";
import { debug } from "../utils/debug.js";
import { isWithinExpiration } from "../utils/date.js";
import { createAdapter } from "./adapter.js";
import { createKeyId } from "./database.js";
import { isAllowedOrigin, safeParseUrl } from "../utils/url.js";
export const lucia = (config) => {
return new Auth(config);
};
const validateConfiguration = (config) => {
const adapterProvided = config.adapter;
if (!adapterProvided) {
logError('Adapter is not defined in configuration ("config.adapter")');
process.exit(1);
}
};
export class Auth {
import { SessionController, SessionCookieController } from "oslo/session";
import { TimeSpan, isWithinExpirationDate } from "oslo";
import { generateRandomString, alphabet } from "oslo/random";
import { verifyRequestOrigin } from "oslo/request";
export class Lucia {
adapter;
sessionCookieConfig;
sessionExpiresIn;
sessionController;
sessionCookieController;
csrfProtection;
env;
passwordHash = {
generate: generateScryptHash,
validate: validateScryptHash
};
middleware = defaultMiddleware();
middleware;
experimental;
constructor(config) {
validateConfiguration(config);
this.adapter = createAdapter(config.adapter);
this.env = config.env;
this.sessionExpiresIn = {
activePeriod: config.sessionExpiresIn?.activePeriod ?? 1000 * 60 * 60 * 24,
idlePeriod: config.sessionExpiresIn?.idlePeriod ?? 1000 * 60 * 60 * 24 * 14
getSessionAttributes;
getUserAttributes;
constructor(adapter, options) {
this.adapter = adapter;
this.middleware = options?.middleware ?? defaultMiddleware();
// we have to use `any` here since TS can't do conditional return types
this.getUserAttributes = (databaseUserAttributes) => {
if (options && options.getUserAttributes) {
return options.getUserAttributes(databaseUserAttributes);
}
return {};
};
this.getUserAttributes = (databaseUser) => {
const defaultTransform = () => {
return {};
};
const transform = config.getUserAttributes ?? defaultTransform;
return transform(databaseUser);
this.getSessionAttributes = (databaseSessionAttributes) => {
if (options && options.getSessionAttributes) {
return options.getSessionAttributes(databaseSessionAttributes);
}
return {};
};
this.getSessionAttributes = (databaseSession) => {
const defaultTransform = () => {
return {};
};
const transform = config.getSessionAttributes ?? defaultTransform;
return transform(databaseSession);
};
this.csrfProtection = config.csrfProtection ?? true;
this.sessionCookieConfig = config.sessionCookie ?? {};
if (config.passwordHash) {
this.passwordHash = config.passwordHash;
this.sessionController = new SessionController(options?.sessionExpiresIn ?? new TimeSpan(30, "d"));
this.sessionCookieController = new SessionCookieController(options?.sessionCookie?.name ?? "auth_session", this.sessionController.expiresIn, options?.sessionCookie);
this.csrfProtection = options?.csrfProtection ?? true;
if (options?.middleware) {
this.middleware = options.middleware;
}
if (config.middleware) {
this.middleware = config.middleware;
}
this.experimental = {
debugMode: config.experimental?.debugMode ?? false
debugMode: options?.experimental?.debugMode ?? false
};
debug.init(this.experimental.debugMode);
}
getUserAttributes;
getSessionAttributes;
transformDatabaseUser = (databaseUser) => {
const attributes = this.getUserAttributes(databaseUser);
return {
...attributes,
userId: databaseUser.id
};
};
transformDatabaseKey = (databaseKey) => {
const [providerId, ...providerUserIdSegments] = databaseKey.id.split(":");
const providerUserId = providerUserIdSegments.join(":");
const userId = databaseKey.user_id;
const isPasswordDefined = !!databaseKey.hashed_password;
return {
providerId,
providerUserId,
userId,
passwordDefined: isPasswordDefined
};
};
transformDatabaseSession = (databaseSession, context) => {
const attributes = this.getSessionAttributes(databaseSession);
const active = isWithinExpiration(databaseSession.active_expires);
return {
...attributes,
user: context.user,
sessionId: databaseSession.id,
activePeriodExpiresAt: new Date(Number(databaseSession.active_expires)),
idlePeriodExpiresAt: new Date(Number(databaseSession.idle_expires)),
state: active ? "active" : "idle",
fresh: context.fresh
};
};
getDatabaseUser = async (userId) => {
const databaseUser = await this.adapter.getUser(userId);
if (!databaseUser) {
throw new LuciaError("AUTH_INVALID_USER_ID");
async getUserSessions(userId) {
const databaseSessions = await this.adapter.getUserSessions(userId);
const sessions = [];
for (const databaseSession of databaseSessions) {
if (!isWithinExpirationDate(databaseSession.expiresAt)) {
continue;
}
sessions.push({
id: databaseSession.sessionId,
expiresAt: databaseSession.expiresAt,
userId: databaseSession.id,
fresh: false,
...this.getSessionAttributes(databaseSession)
});
}
return databaseUser;
};
getDatabaseSession = async (sessionId) => {
const databaseSession = await this.adapter.getSession(sessionId);
return sessions;
}
async validateSession(sessionId) {
const [databaseSession, databaseUser] = await this.adapter.getSessionAndUser(sessionId);
if (!databaseSession) {
debug.session.fail("Session not found", sessionId);
throw new LuciaError("AUTH_INVALID_SESSION_ID");
return { session: null, user: null };
}
if (!isValidDatabaseSession(databaseSession)) {
debug.session.fail(`Session expired at ${new Date(Number(databaseSession.idle_expires))}`, sessionId);
throw new LuciaError("AUTH_INVALID_SESSION_ID");
if (!databaseUser) {
await this.adapter.deleteSession(databaseSession.sessionId);
debug.session.fail("Session not found", sessionId);
return { session: null, user: null };
}
return databaseSession;
};
getDatabaseSessionAndUser = async (sessionId) => {
if (this.adapter.getSessionAndUser) {
const [databaseSession, databaseUser] = await this.adapter.getSessionAndUser(sessionId);
if (!databaseSession) {
debug.session.fail("Session not found", sessionId);
throw new LuciaError("AUTH_INVALID_SESSION_ID");
}
if (!isValidDatabaseSession(databaseSession)) {
debug.session.fail(`Session expired at ${new Date(Number(databaseSession.idle_expires))}`, sessionId);
throw new LuciaError("AUTH_INVALID_SESSION_ID");
}
return [databaseSession, databaseUser];
const sessionState = this.sessionController.getSessionState(databaseSession.expiresAt);
if (sessionState === "expired") {
debug.session.fail("Session expired", sessionId);
await this.adapter.deleteSession(databaseSession.sessionId);
return { session: null, user: null };
}
const databaseSession = await this.getDatabaseSession(sessionId);
const databaseUser = await this.getDatabaseUser(databaseSession.user_id);
return [databaseSession, databaseUser];
};
validateSessionIdArgument = (sessionId) => {
if (!sessionId) {
debug.session.fail("Empty session id");
throw new LuciaError("AUTH_INVALID_SESSION_ID");
let expiresAt = databaseSession.expiresAt;
let fresh = false;
if (sessionState === "idle") {
expiresAt = this.sessionController.createExpirationDate();
await this.adapter.updateSession(databaseSession.sessionId, {
expiresAt
});
fresh = true;
}
};
getNewSessionExpiration = (sessionExpiresIn) => {
const activePeriodExpiresAt = new Date(new Date().getTime() +
(sessionExpiresIn?.activePeriod ?? this.sessionExpiresIn.activePeriod));
const idlePeriodExpiresAt = new Date(activePeriodExpiresAt.getTime() +
(sessionExpiresIn?.idlePeriod ?? this.sessionExpiresIn.idlePeriod));
return { activePeriodExpiresAt, idlePeriodExpiresAt };
};
getUser = async (userId) => {
const databaseUser = await this.getDatabaseUser(userId);
const user = this.transformDatabaseUser(databaseUser);
return user;
};
createUser = async (options) => {
const userId = options.userId ?? generateRandomString(15);
const userAttributes = options.attributes ?? {};
const databaseUser = {
...userAttributes,
id: userId
const session = {
id: databaseSession.sessionId,
userId: databaseSession.id,
fresh,
expiresAt,
...this.getSessionAttributes(databaseSession.attributes)
};
if (options.key === null) {
await this.adapter.setUser(databaseUser, null);
return this.transformDatabaseUser(databaseUser);
}
const keyId = createKeyId(options.key.providerId, options.key.providerUserId);
const password = options.key.password;
const hashedPassword = password === null ? null : await this.passwordHash.generate(password);
await this.adapter.setUser(databaseUser, {
id: keyId,
user_id: userId,
hashed_password: hashedPassword
const user = {
...this.getUserAttributes(databaseUser),
id: databaseUser.id
};
return { user, session };
}
async createSession(userId, attributes) {
const sessionId = generateRandomString(40, alphabet("0-9", "a-z"));
const sessionExpiresAt = this.sessionController.createExpirationDate();
await this.adapter.setSession({
sessionId,
id: userId,
expiresAt: sessionExpiresAt,
attributes
});
return this.transformDatabaseUser(databaseUser);
};
updateUserAttributes = async (userId, attributes) => {
await this.adapter.updateUser(userId, attributes);
return await this.getUser(userId);
};
deleteUser = async (userId) => {
await this.adapter.deleteSessionsByUserId(userId);
await this.adapter.deleteKeysByUserId(userId);
await this.adapter.deleteUser(userId);
};
useKey = async (providerId, providerUserId, password) => {
const keyId = createKeyId(providerId, providerUserId);
const databaseKey = await this.adapter.getKey(keyId);
if (!databaseKey) {
debug.key.fail("Key not found", keyId);
throw new LuciaError("AUTH_INVALID_KEY_ID");
}
const hashedPassword = databaseKey.hashed_password;
if (hashedPassword !== null) {
debug.key.info("Key includes password");
if (!password) {
debug.key.fail("Key password not provided", keyId);
throw new LuciaError("AUTH_INVALID_PASSWORD");
}
const validPassword = await this.passwordHash.validate(password, hashedPassword);
if (!validPassword) {
debug.key.fail("Incorrect key password", password);
throw new LuciaError("AUTH_INVALID_PASSWORD");
}
debug.key.notice("Validated key password");
}
else {
if (password !== null) {
debug.key.fail("Incorrect key password", password);
throw new LuciaError("AUTH_INVALID_PASSWORD");
}
debug.key.info("No password included in key");
}
debug.key.success("Validated key", keyId);
return this.transformDatabaseKey(databaseKey);
};
getSession = async (sessionId) => {
this.validateSessionIdArgument(sessionId);
const [databaseSession, databaseUser] = await this.getDatabaseSessionAndUser(sessionId);
const user = this.transformDatabaseUser(databaseUser);
return this.transformDatabaseSession(databaseSession, {
user,
fresh: false
});
};
getAllUserSessions = async (userId) => {
const [user, databaseSessions] = await Promise.all([
this.getUser(userId),
await this.adapter.getSessionsByUserId(userId)
]);
const validStoredUserSessions = databaseSessions
.filter((databaseSession) => {
return isValidDatabaseSession(databaseSession);
})
.map((databaseSession) => {
return this.transformDatabaseSession(databaseSession, {
user,
fresh: false
});
});
return validStoredUserSessions;
};
validateSession = async (sessionId) => {
this.validateSessionIdArgument(sessionId);
const [databaseSession, databaseUser] = await this.getDatabaseSessionAndUser(sessionId);
const user = this.transformDatabaseUser(databaseUser);
const session = this.transformDatabaseSession(databaseSession, {
user,
fresh: false
});
if (session.state === "active") {
debug.session.success("Validated session", session.sessionId);
return session;
}
const { activePeriodExpiresAt, idlePeriodExpiresAt } = this.getNewSessionExpiration();
await this.adapter.updateSession(session.sessionId, {
active_expires: activePeriodExpiresAt.getTime(),
idle_expires: idlePeriodExpiresAt.getTime()
});
const renewedDatabaseSession = {
...session,
idlePeriodExpiresAt,
activePeriodExpiresAt,
fresh: true
};
return renewedDatabaseSession;
};
createSession = async (options) => {
const { activePeriodExpiresAt, idlePeriodExpiresAt } = this.getNewSessionExpiration();
const userId = options.userId;
const sessionId = options?.sessionId ?? generateRandomString(40);
const attributes = options.attributes;
const databaseSession = {
...attributes,
const session = {
id: sessionId,
user_id: userId,
active_expires: activePeriodExpiresAt.getTime(),
idle_expires: idlePeriodExpiresAt.getTime()
userId,
fresh: true,
expiresAt: sessionExpiresAt,
...this.getSessionAttributes(attributes)
};
const [user] = await Promise.all([
this.getUser(userId),
this.adapter.setSession(databaseSession)
]);
return this.transformDatabaseSession(databaseSession, {
user,
fresh: false
});
};
updateSessionAttributes = async (sessionId, attributes) => {
this.validateSessionIdArgument(sessionId);
await this.adapter.updateSession(sessionId, attributes);
return this.getSession(sessionId);
};
invalidateSession = async (sessionId) => {
this.validateSessionIdArgument(sessionId);
return session;
}
// public updateSessionAttributes = async (
// sessionId: string,
// attributes: Partial<DatabaseSessionAttributes>
// ): Promise<Session> => {
// this.validateSessionIdArgument(sessionId);
// await this.adapter.updateSession(sessionId, attributes);
// return this.getSession(sessionId);
// };
async invalidateSession(sessionId) {
await this.adapter.deleteSession(sessionId);
debug.session.notice("Invalidated session", sessionId);
};
invalidateAllUserSessions = async (userId) => {
await this.adapter.deleteSessionsByUserId(userId);
};
deleteDeadUserSessions = async (userId) => {
const databaseSessions = await this.adapter.getSessionsByUserId(userId);
const deadSessionIds = databaseSessions
.filter((databaseSession) => {
return !isValidDatabaseSession(databaseSession);
})
.map((databaseSession) => databaseSession.id);
await Promise.all(deadSessionIds.map((deadSessionId) => {
this.adapter.deleteSession(deadSessionId);
}));
};
/**
* @deprecated To be removed in next major release
*/
validateRequestOrigin = (request) => {
if (request.method === null) {
debug.request.fail("Request method unavailable");
throw new LuciaError("AUTH_INVALID_REQUEST");
}
if (request.url === null) {
debug.request.fail("Request url unavailable");
throw new LuciaError("AUTH_INVALID_REQUEST");
}
if (request.method.toUpperCase() !== "GET" &&
request.method.toUpperCase() !== "HEAD") {
const requestOrigin = request.headers.origin;
if (!requestOrigin) {
debug.request.fail("No request origin available");
throw new LuciaError("AUTH_INVALID_REQUEST");
}
try {
const url = safeParseUrl(request.url);
const allowedSubDomains = typeof this.csrfProtection === "object"
? this.csrfProtection.allowedSubDomains ?? []
: [];
if (url === null ||
!isAllowedOrigin(requestOrigin, url.origin, allowedSubDomains)) {
throw new LuciaError("AUTH_INVALID_REQUEST");
}
debug.request.info("Valid request origin", requestOrigin);
}
catch {
debug.request.fail("Invalid origin string", requestOrigin);
// failed to parse url
throw new LuciaError("AUTH_INVALID_REQUEST");
}
}
else {
debug.request.notice("Skipping CSRF check");
}
};
readSessionCookie = (cookieHeader) => {
if (!cookieHeader) {
debug.request.info("No session cookie found");
return null;
}
const cookies = parseCookie(cookieHeader);
const sessionCookieName = this.sessionCookieConfig.name ?? DEFAULT_SESSION_COOKIE_NAME;
const sessionId = cookies[sessionCookieName] ?? null;
}
async invalidateUserSessions(userId) {
await this.adapter.deleteUserSessions(userId);
}
readSessionCookie(cookieHeader) {
const sessionId = this.sessionCookieController.parseCookies(cookieHeader);
if (sessionId) {

@@ -372,8 +142,4 @@ debug.request.info("Found session cookie", sessionId);

return sessionId;
};
readBearerToken = (authorizationHeader) => {
if (!authorizationHeader) {
debug.request.info("No token found in authorization header");
return null;
}
}
readBearerToken(authorizationHeader) {
const [authScheme, token] = authorizationHeader.split(" ");

@@ -385,70 +151,64 @@ if (authScheme !== "Bearer") {

return token ?? null;
};
handleRequest = (
// cant reference middleware type with Lucia.Auth
...args) => {
}
handleRequest(...args) {
const middleware = this.middleware;
const sessionCookieName = this.sessionCookieConfig.name ?? DEFAULT_SESSION_COOKIE_NAME;
return new AuthRequest(this, {
csrfProtection: this.csrfProtection,
requestContext: transformRequestContext(middleware({
args,
env: this.env,
sessionCookieName: sessionCookieName
}))
const requestContext = middleware({
args,
sessionCookieName: this.sessionCookieController.cookieName
});
};
createSessionCookie = (session) => {
return createSessionCookie(session, {
env: this.env,
cookie: this.sessionCookieConfig
});
};
createKey = async (options) => {
const keyId = createKeyId(options.providerId, options.providerUserId);
let hashedPassword = null;
if (options.password !== null) {
hashedPassword = await this.passwordHash.generate(options.password);
debug.request.init(requestContext.request.method, requestContext.request.url ?? "(url unknown)");
const authorizationHeader = requestContext.request.headers.get("Authorization");
let bearerToken = authorizationHeader;
if (authorizationHeader) {
const parts = authorizationHeader.split(" ");
if (parts.length === 2 && parts[0] === "Bearer") {
bearerToken = parts[1];
}
}
const userId = options.userId;
await this.adapter.setKey({
id: keyId,
user_id: userId,
hashed_password: hashedPassword
});
return {
providerId: options.providerId,
providerUserId: options.providerUserId,
passwordDefined: !!options.password,
userId
};
};
deleteKey = async (providerId, providerUserId) => {
const keyId = createKeyId(providerId, providerUserId);
await this.adapter.deleteKey(keyId);
};
getKey = async (providerId, providerUserId) => {
const keyId = createKeyId(providerId, providerUserId);
const databaseKey = await this.adapter.getKey(keyId);
if (!databaseKey) {
throw new LuciaError("AUTH_INVALID_KEY_ID");
if (this.csrfProtection !== false) {
const options = this.csrfProtection === true ? {} : this.csrfProtection;
const validRequestOrigin = this.verifyRequestOrigin(requestContext, options);
if (!validRequestOrigin) {
return new AuthRequest(this, null, bearerToken, requestContext.setCookie);
}
}
const key = this.transformDatabaseKey(databaseKey);
return key;
};
getAllUserKeys = async (userId) => {
const [databaseKeys] = await Promise.all([
await this.adapter.getKeysByUserId(userId),
this.getUser(userId)
]);
return databaseKeys.map((databaseKey) => this.transformDatabaseKey(databaseKey));
};
updateKeyPassword = async (providerId, providerUserId, password) => {
const keyId = createKeyId(providerId, providerUserId);
const hashedPassword = password === null ? null : await this.passwordHash.generate(password);
await this.adapter.updateKey(keyId, {
hashed_password: hashedPassword
});
return await this.getKey(providerId, providerUserId);
};
const sessionCookie = requestContext.sessionCookie ??
this.sessionCookieController.parseCookies(requestContext.request.headers.get("Cookie") ?? "");
return new AuthRequest(this, sessionCookie, bearerToken, requestContext.setCookie);
}
verifyRequestOrigin(requestContext, options) {
const whitelist = ["GET", "HEAD", "OPTIONS", "TRACE"];
const allowedMethod = whitelist.some((val) => val === requestContext.request.method.toUpperCase());
if (allowedMethod) {
return true;
}
const requestOrigin = requestContext.request.headers.get("Origin");
if (!requestOrigin) {
debug.request.fail("Origin header unavailable");
return false;
}
const allowedDomains = options.allowedDomains ?? [];
const hostHeader = requestContext.request.headers.get(options.hostHeader ?? "Host");
if (hostHeader) {
allowedDomains.push(hostHeader);
}
if (requestContext.request.url !== undefined) {
allowedDomains.push(requestContext.request.url);
}
debug.request.info("Allowed domains", allowedDomains.join(", "));
debug.request.info("Origin", requestOrigin ?? "(Origin unknown)");
const validOrigin = verifyRequestOrigin(requestOrigin, allowedDomains);
if (validOrigin) {
debug.request.info("Valid request origin");
return true;
}
debug.request.info("Invalid request origin");
return false;
}
createSessionCookie(sessionId) {
return this.sessionCookieController.createSessionCookie(sessionId);
}
createBlankSessionCookie() {
return this.sessionCookieController.createBlankSessionCookie();
}
}

@@ -1,57 +0,28 @@

import type { Auth, Env, Session } from "./index.js";
import type { Cookie } from "./cookie.js";
export type LuciaRequest = {
method: string;
url?: string;
headers: Pick<Headers, "get">;
};
export type RequestContext = {
sessionCookie?: string | null;
request: LuciaRequest;
setCookie: (cookie: Cookie) => void;
};
export type Middleware<Args extends any[] = any> = (context: {
args: Args;
env: Env;
sessionCookieName: string;
}) => MiddlewareRequestContext;
type MiddlewareRequestContext = Omit<RequestContext, "request"> & {
sessionCookie?: string | null;
request: {
method: string;
url?: string;
headers: Pick<Headers, "get"> | {
origin: string | null;
cookie: string | null;
authorization: string | null;
};
storedSessionCookie?: string | null;
};
setCookie: (cookie: Cookie) => void;
};
export type CSRFProtectionConfiguration = {
host?: string;
hostHeader?: string;
allowedSubDomains?: string[] | "*";
};
export declare class AuthRequest<_Auth extends Auth = any> {
import type { SessionCookie } from "oslo/session";
import type { Lucia, Session, User } from "./index.js";
export declare class AuthRequest<_Lucia extends Lucia = Lucia> {
private auth;
private requestContext;
constructor(auth: _Auth, config: {
requestContext: RequestContext;
csrfProtection: boolean | CSRFProtectionConfiguration;
});
private sessionCookie;
private bearerToken;
private setCookie;
constructor(auth: _Lucia, sessionCookie: string | null, bearerToken: string | null, setCookie: (cookie: SessionCookie) => void);
private validatePromise;
private validateBearerTokenPromise;
private storedSessionId;
private bearerToken;
setSession: (session: Session | null) => void;
private maybeSetSession;
private setSessionCookie;
validate: () => Promise<Session | null>;
validateBearerToken: () => Promise<Session | null>;
setSessionCookie(sessionId: string): void;
deleteSessionCookie(): void;
validate(): Promise<{
user: User;
session: Session;
} | {
user: null;
session: null;
}>;
validateBearerToken(): Promise<{
user: User;
session: Session;
} | {
user: null;
session: null;
}>;
invalidate(): void;
private isValidRequestOrigin;
}
export declare const transformRequestContext: ({ request, setCookie, sessionCookie }: MiddlewareRequestContext) => RequestContext;
export {};

@@ -1,105 +0,54 @@

import { debug } from "../utils/debug.js";
import { LuciaError } from "./error.js";
import { createHeadersFromObject } from "../utils/request.js";
import { isAllowedOrigin, safeParseUrl } from "../utils/url.js";
export class AuthRequest {
auth;
requestContext;
constructor(auth, config) {
debug.request.init(config.requestContext.request.method, config.requestContext.request.url ?? "(url unknown)");
sessionCookie;
bearerToken;
setCookie;
constructor(auth, sessionCookie, bearerToken, setCookie) {
this.auth = auth;
this.requestContext = config.requestContext;
const csrfProtectionConfig = typeof config.csrfProtection === "object" ? config.csrfProtection : {};
const csrfProtectionEnabled = config.csrfProtection !== false;
if (!csrfProtectionEnabled ||
this.isValidRequestOrigin(csrfProtectionConfig)) {
this.storedSessionId =
this.requestContext.sessionCookie ??
auth.readSessionCookie(this.requestContext.request.headers.get("Cookie"));
}
else {
this.storedSessionId = null;
}
this.bearerToken = auth.readBearerToken(this.requestContext.request.headers.get("Authorization"));
this.sessionCookie = sessionCookie;
this.bearerToken = bearerToken;
this.setCookie = setCookie;
}
validatePromise = null;
validateBearerTokenPromise = null;
storedSessionId;
bearerToken;
setSession = (session) => {
const sessionId = session?.sessionId ?? null;
if (this.storedSessionId === sessionId)
setSessionCookie(sessionId) {
if (this.sessionCookie !== sessionId) {
this.validatePromise = null;
}
this.setCookie(this.auth.createSessionCookie(sessionId));
}
deleteSessionCookie() {
if (this.sessionCookie === null)
return;
this.sessionCookie = null;
this.validatePromise = null;
this.setSessionCookie(session);
};
maybeSetSession = (session) => {
try {
this.setSession(session);
}
catch {
// ignore error
// some middleware throw error
}
};
setSessionCookie = (session) => {
const sessionId = session?.sessionId ?? null;
if (this.storedSessionId === sessionId)
return;
this.storedSessionId = sessionId;
this.requestContext.setCookie(this.auth.createSessionCookie(session));
if (session) {
debug.request.notice("Session cookie stored", session.sessionId);
}
else {
debug.request.notice("Session cookie deleted");
}
};
validate = async () => {
if (this.validatePromise) {
debug.request.info("Using cached result for session validation");
return this.validatePromise;
}
this.validatePromise = new Promise(async (resolve, reject) => {
if (!this.storedSessionId)
return resolve(null);
try {
const session = await this.auth.validateSession(this.storedSessionId);
if (session.fresh) {
this.maybeSetSession(session);
this.setCookie(this.auth.createBlankSessionCookie());
}
async validate() {
if (!this.validatePromise) {
this.validatePromise = new Promise(async (resolve) => {
if (!this.sessionCookie) {
return resolve({ session: null, user: null });
}
return resolve(session);
}
catch (e) {
if (e instanceof LuciaError &&
e.message === "AUTH_INVALID_SESSION_ID") {
this.maybeSetSession(null);
return resolve(null);
const result = await this.auth.validateSession(this.sessionCookie);
if (result.session && result.session.fresh) {
const sessionCookie = this.auth.createSessionCookie(result.session.id);
this.setCookie(sessionCookie);
}
return reject(e);
}
});
return resolve(result);
});
}
return await this.validatePromise;
};
validateBearerToken = async () => {
if (this.validateBearerTokenPromise) {
debug.request.info("Using cached result for bearer token validation");
return this.validatePromise;
}
async validateBearerToken() {
if (!this.validateBearerTokenPromise) {
this.validateBearerTokenPromise = new Promise(async (resolve, reject) => {
if (!this.bearerToken) {
return resolve({ session: null, user: null });
}
return await this.auth.validateSession(this.bearerToken);
});
}
this.validatePromise = new Promise(async (resolve, reject) => {
if (!this.bearerToken)
return resolve(null);
try {
const session = await this.auth.validateSession(this.bearerToken);
return resolve(session);
}
catch (e) {
if (e instanceof LuciaError) {
return resolve(null);
}
return reject(e);
}
});
return await this.validatePromise;
};
return await this.validateBearerTokenPromise;
}
invalidate() {

@@ -109,47 +58,2 @@ this.validatePromise = null;

}
isValidRequestOrigin = (config) => {
const request = this.requestContext.request;
const whitelist = ["GET", "HEAD", "OPTIONS", "TRACE"];
if (whitelist.some((val) => val === request.method.toUpperCase())) {
return true;
}
const requestOrigin = request.headers.get("Origin");
if (!requestOrigin)
return false;
if (!requestOrigin) {
debug.request.fail("No request origin available");
return false;
}
let host = null;
if (config.host !== undefined) {
host = config.host ?? null;
}
else if (request.url !== null && request.url !== undefined) {
host = safeParseUrl(request.url)?.host ?? null;
}
else {
host = request.headers.get(config.hostHeader ?? "Host");
}
debug.request.info("Host", host ?? "(Host unknown)");
if (host !== null &&
isAllowedOrigin(requestOrigin, host, config.allowedSubDomains ?? [])) {
debug.request.info("Valid request origin", requestOrigin);
return true;
}
debug.request.info("Invalid request origin", requestOrigin);
return false;
};
}
export const transformRequestContext = ({ request, setCookie, sessionCookie }) => {
return {
request: {
url: request.url,
method: request.method,
headers: "authorization" in request.headers
? createHeadersFromObject(request.headers)
: request.headers
},
setCookie,
sessionCookie: sessionCookie ?? request.storedSessionCookie
};
};

@@ -1,13 +0,18 @@

export { lucia } from "./auth/index.js";
export { DEFAULT_SESSION_COOKIE_NAME } from "./auth/cookie.js";
export { LuciaError } from "./auth/error.js";
export { createKeyId } from "./auth/database.js";
export type GlobalAuth = Lucia.Auth;
export type GlobalDatabaseUserAttributes = Lucia.DatabaseUserAttributes;
export type GlobalDatabaseSessionAttributes = Lucia.DatabaseSessionAttributes;
export type { User, Key, Session, Configuration, Env, Auth } from "./auth/index.js";
export type { Adapter, InitializeAdapter, UserAdapter, SessionAdapter } from "./auth/adapter.js";
export type { UserSchema, KeySchema, SessionSchema } from "./auth/database.js";
export type { RequestContext, Middleware, AuthRequest } from "./auth/request.js";
export type { Cookie } from "./auth/cookie.js";
export type { LuciaErrorConstructor } from "./auth/error.js";
export { Lucia } from "./auth/index.js";
export { AuthRequest } from "./auth/request.js";
export { generateScryptHash as generateLegacyLuciaPasswordHash, verifyScryptHash as verifyLegacyLuciaPasswordHash } from "./utils/crypto.js";
export { TimeSpan } from "oslo";
export type { User, Session, ExperimentalOptions, SessionCookieOptions, CSRFProtectionOptions, RequestContext, Middleware } from "./auth/index.js";
export type { DatabaseSession, DatabaseUser, Adapter, SessionAdapter } from "./auth/database.js";
export interface Register {
}
import type { Lucia } from "./auth/index.js";
export type RegisteredLucia = Register extends {
Lucia: infer _Lucia;
} ? _Lucia extends Lucia ? _Lucia : Lucia : Lucia;
export type DatabaseUserAttributes = Register extends {
DatabaseUserAttributes: {};
} ? Register["DatabaseUserAttributes"] : {};
export type DatabaseSessionAttributes = Register extends {
DatabaseSessionAttributes: {};
} ? Register["DatabaseSessionAttributes"] : {};

@@ -1,4 +0,4 @@

export { lucia } from "./auth/index.js";
export { DEFAULT_SESSION_COOKIE_NAME } from "./auth/cookie.js";
export { LuciaError } from "./auth/error.js";
export { createKeyId } from "./auth/database.js";
export { Lucia } from "./auth/index.js";
export { AuthRequest } from "./auth/request.js";
export { generateScryptHash as generateLegacyLuciaPasswordHash, verifyScryptHash as verifyLegacyLuciaPasswordHash } from "./utils/crypto.js";
export { TimeSpan } from "oslo";

@@ -1,11 +0,11 @@

import type { CookieAttributes } from "../utils/cookie.js";
import type { Middleware, RequestContext } from "../auth/request.js";
type NodeIncomingMessage = {
import type { CookieAttributes } from "oslo/cookie";
import type { Middleware, RequestContext } from "../auth/index.js";
interface NodeIncomingMessage {
method?: string;
headers: Record<string, string | string[] | undefined>;
};
type NodeOutGoingMessage = {
}
interface NodeOutGoingMessage {
getHeader: (name: string) => string | string[] | number | undefined;
setHeader: (name: string, value: string | number | readonly string[]) => void;
};
}
export declare const node: () => Middleware<[

@@ -15,19 +15,19 @@ NodeIncomingMessage,

]>;
type ExpressRequest = {
interface ExpressRequest {
method: string;
headers: Record<string, string | string[] | undefined>;
};
type ExpressResponse = {
}
interface ExpressResponse {
cookie: (name: string, val: string, options?: CookieAttributes) => void;
};
}
export declare const express: () => Middleware<[ExpressRequest, ExpressResponse]>;
type FastifyRequest = {
interface FastifyRequest {
method: string;
headers: Record<string, string | string[] | undefined>;
};
type FastifyReply = {
}
interface FastifyReply {
header: (name: string, val: any) => void;
};
}
export declare const fastify: () => Middleware<[FastifyRequest, FastifyReply]>;
type SvelteKitRequestEvent = {
interface SvelteKitRequestEvent {
request: Request;

@@ -38,3 +38,3 @@ cookies: {

};
};
}
export declare const sveltekit: () => Middleware<[SvelteKitRequestEvent]>;

@@ -51,3 +51,3 @@ type AstroAPIContext = {

export declare const astro: () => Middleware<[AstroAPIContext]>;
type QwikRequestEvent = {
interface QwikRequestEvent {
request: Request;

@@ -60,5 +60,5 @@ cookie: {

};
};
}
export declare const qwik: () => Middleware<[QwikRequestEvent]>;
type ElysiaContext = {
interface ElysiaContext {
request: Request;

@@ -70,10 +70,10 @@ set: {

};
};
}
export declare const elysia: () => Middleware<[ElysiaContext]>;
export declare const lucia: () => Middleware<[RequestContext]>;
export declare const web: () => Middleware<[Request]>;
type NextJsPagesServerContext = {
interface NextJsPagesServerContext {
req: NodeIncomingMessage;
res?: NodeOutGoingMessage;
};
}
type NextCookie = {

@@ -90,20 +90,17 @@ name: string;

};
type NextRequest = Request & {
interface NextRequest extends Request {
cookies: {
get: (name: string) => NextCookie;
};
};
type NextJsAppServerContext = {
}
interface NextJsAppServerContext {
cookies: NextCookiesFunction;
request: NextRequest | null;
};
export declare const nextjs: () => Middleware<[
NextJsPagesServerContext | NextJsAppServerContext | NextRequest
]>;
type NextJsAppServerContext_V3 = {
}
interface NextJsAppServerContext {
headers: NextHeadersFunction;
cookies: NextCookiesFunction;
};
export declare const nextjs_future: () => Middleware<[NextJsPagesServerContext] | [NextRequest] | [requestMethod: string, context: NextJsAppServerContext_V3]>;
type H3Event = {
}
export declare const nextjs: () => Middleware<[NextJsPagesServerContext] | [NextRequest] | [requestMethod: string, context: NextJsAppServerContext]>;
interface H3Event {
node: {

@@ -113,5 +110,5 @@ req: NodeIncomingMessage;

};
};
}
export declare const h3: () => Middleware<[H3Event]>;
type HonoContext = {
interface HonoContext {
req: {

@@ -123,4 +120,4 @@ url: string;

header: (name: string, value: string) => void;
};
}
export declare const hono: () => Middleware<[HonoContext]>;
export {};

@@ -133,60 +133,2 @@ import { createHeadersFromObject } from "../utils/request.js";

export const nextjs = () => {
return ({ args, sessionCookieName, env }) => {
const [serverContext] = args;
if ("cookies" in serverContext) {
// for some reason `"request" in NextRequest` returns true???
const request = typeof serverContext.cookies === "function"
? serverContext.request
: serverContext;
const readonlyCookieStore = typeof serverContext.cookies === "function"
? serverContext.cookies()
: serverContext.cookies;
const sessionCookie = readonlyCookieStore.get(sessionCookieName)?.value ?? null;
const requestContext = {
request: request ?? {
method: "GET",
headers: new Headers()
},
sessionCookie,
setCookie: (cookie) => {
if (typeof serverContext.cookies !== "function")
return;
const cookieStore = serverContext.cookies();
if (!cookieStore.set)
return;
try {
cookieStore.set(cookie.name, cookie.value, cookie.attributes);
}
catch {
// ignore - set() is not available
}
}
};
return requestContext;
}
const req = "req" in serverContext ? serverContext.req : serverContext;
const res = "res" in serverContext ? serverContext.res : null;
const request = {
method: req.method ?? "",
headers: createHeadersFromObject(req.headers)
};
return {
request,
setCookie: (cookie) => {
if (!res)
return;
const setCookieHeaderValues = res
.getHeader("Set-Cookie")
?.toString()
.split(",")
.filter((val) => val) ?? [];
res.setHeader("Set-Cookie", [
cookie.serialize(),
...setCookieHeaderValues
]);
}
};
};
};
export const nextjs_future = () => {
return ({ args, sessionCookieName }) => {

@@ -247,8 +189,7 @@ if (args.length === 2) {

const nodeMiddleware = node();
return ({ args, sessionCookieName, env }) => {
return ({ args, sessionCookieName }) => {
const [context] = args;
return nodeMiddleware({
args: [context.node.req, context.node.res],
sessionCookieName,
env
sessionCookieName
});

@@ -255,0 +196,0 @@ };

@@ -1,4 +0,2 @@

export declare const generateRandomString: (length: number, alphabet?: string) => string;
export declare const generateScryptHash: (s: string) => Promise<string>;
export declare const validateScryptHash: (s: string, hash: string) => Promise<boolean>;
export declare const convertUint8ArrayToHex: (arr: Uint8Array) => string;
export declare const verifyScryptHash: (s: string, hash: string) => Promise<boolean>;

@@ -1,15 +0,10 @@

import { LuciaError } from "../auth/error.js";
import { scryptAsync as scrypt } from "@noble/hashes/scrypt";
import { customAlphabet } from "nanoid";
export const generateRandomString = (length, alphabet) => {
const DEFAULT_ALPHABET = "abcdefghijklmnopqrstuvwxyz1234567890";
const customNanoid = customAlphabet(alphabet ?? DEFAULT_ALPHABET);
return customNanoid(length);
};
import { encodeHex, decodeHex } from "oslo/encoding";
import { constantTimeEqual } from "oslo/crypto";
import { scrypt } from "../scrypt/index.js";
export const generateScryptHash = async (s) => {
const salt = generateRandomString(16);
const key = await hashWithScrypt(s.normalize("NFKC"), salt);
return `s2:${salt}:${key}`;
const salt = encodeHex(crypto.getRandomValues(new Uint8Array(16)));
const key = await generateScryptKey(s.normalize("NFKC"), salt);
return `s2:${salt}:${encodeHex(key)}`;
};
const hashWithScrypt = async (s, salt, blockSize = 16) => {
const generateScryptKey = async (s, salt, blockSize = 16) => {
const keyUint8Array = await scrypt(new TextEncoder().encode(s), new TextEncoder().encode(salt), {

@@ -21,16 +16,10 @@ N: 16384,

});
return convertUint8ArrayToHex(keyUint8Array);
return keyUint8Array;
};
export const validateScryptHash = async (s, hash) => {
// detect bcrypt hash
// lucia used bcrypt in one of the beta versions
// TODO: remove in v3
if (hash.startsWith("$2a")) {
throw new LuciaError("AUTH_OUTDATED_PASSWORD");
}
export const verifyScryptHash = async (s, hash) => {
const arr = hash.split(":");
if (arr.length === 2) {
const [salt, key] = arr;
const targetKey = await hashWithScrypt(s.normalize("NFKC"), salt, 8);
const result = constantTimeEqual(targetKey, key);
const targetKey = await generateScryptKey(s.normalize("NFKC"), salt, 8);
const result = constantTimeEqual(targetKey, decodeHex(key));
return result;

@@ -42,22 +31,6 @@ }

if (version === "s2") {
const targetKey = await hashWithScrypt(s.normalize("NFKC"), salt);
const result = constantTimeEqual(targetKey, key);
return result;
const targetKey = await generateScryptKey(s.normalize("NFKC"), salt);
return constantTimeEqual(targetKey, decodeHex(key));
}
return false;
};
const constantTimeEqual = (a, b) => {
if (a.length !== b.length) {
return false;
}
const aUint8Array = new TextEncoder().encode(a);
const bUint8Array = new TextEncoder().encode(b);
let c = 0;
for (let i = 0; i < a.length; i++) {
c |= aUint8Array[i] ^ bUint8Array[i]; // ^: XOR operator
}
return c === 0;
};
export const convertUint8ArrayToHex = (arr) => {
return [...arr].map((x) => x.toString(16).padStart(2, "0")).join("");
};
{
"name": "lucia",
"version": "2.7.3",
"version": "3.0.0-beta.0",
"description": "A simple and flexible authentication library",

@@ -23,4 +23,3 @@ "main": "dist/index.js",

"./middleware": "./dist/middleware/index.js",
"./polyfill/node": "./dist/polyfill/node.js",
"./utils": "./dist/utils/index.js"
"./polyfill/node": "./dist/polyfill/node.js"
},

@@ -34,5 +33,2 @@ "typesVersions": {

"dist/polyfill/node.d.ts"
],
"utils": [
"dist/utils/index.d.ts"
]

@@ -54,4 +50,3 @@ }

"dependencies": {
"@noble/hashes": "1.3.2",
"nanoid": "5.0.1"
"oslo": "^0.19.0"
},

@@ -58,0 +53,0 @@ "scripts": {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc