First running version
This commit is contained in:
118
platform_impl/src/passkey/storage.ts
Normal file
118
platform_impl/src/passkey/storage.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* Passkey Storage Interface and In-Memory Implementation
|
||||
* Spec Section 3.7, 7.3.1
|
||||
*/
|
||||
|
||||
export interface PasskeyCredential {
|
||||
id: string; // base64-encoded credential ID
|
||||
publicKey: string; // base64-encoded public key
|
||||
counter: number; // signature counter (prevents cloned authenticators)
|
||||
transports?: string[]; // e.g., ["platform", "usb"]
|
||||
createdAt: string; // ISO 8601 timestamp
|
||||
lastUsed?: string; // ISO 8601 timestamp
|
||||
}
|
||||
|
||||
export interface PasskeyUser {
|
||||
userId: string;
|
||||
displayName: string;
|
||||
credentials: PasskeyCredential[];
|
||||
createdAt: string; // ISO 8601 timestamp
|
||||
}
|
||||
|
||||
export interface IPasskeyStorage {
|
||||
// User operations
|
||||
getUser(userId: string): Promise<PasskeyUser | null>;
|
||||
createUser(userId: string, displayName: string): Promise<PasskeyUser>;
|
||||
userExists(userId: string): Promise<boolean>;
|
||||
|
||||
// Credential operations
|
||||
addCredential(userId: string, credential: PasskeyCredential): Promise<void>;
|
||||
getCredential(credentialId: string): Promise<PasskeyCredential | null>;
|
||||
getCredentialsByUserId(userId: string): Promise<PasskeyCredential[]>;
|
||||
updateCredentialCounter(credentialId: string, counter: number): Promise<void>;
|
||||
deleteCredential(userId: string, credentialId: string): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* In-Memory Passkey Storage Implementation
|
||||
* Suitable for demo/localhost mode. For production, implement persistent storage
|
||||
* (e.g., MongoDB, PostgreSQL, etc.)
|
||||
*/
|
||||
export class InMemoryPasskeyStorage implements IPasskeyStorage {
|
||||
private users = new Map<string, PasskeyUser>();
|
||||
|
||||
async getUser(userId: string): Promise<PasskeyUser | null> {
|
||||
return this.users.get(userId) ?? null;
|
||||
}
|
||||
|
||||
async createUser(userId: string, displayName: string): Promise<PasskeyUser> {
|
||||
if (this.users.has(userId)) {
|
||||
throw new Error(`User ${userId} already exists`);
|
||||
}
|
||||
|
||||
const user: PasskeyUser = {
|
||||
userId,
|
||||
displayName,
|
||||
credentials: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
this.users.set(userId, user);
|
||||
return user;
|
||||
}
|
||||
|
||||
async userExists(userId: string): Promise<boolean> {
|
||||
return this.users.has(userId);
|
||||
}
|
||||
|
||||
async addCredential(userId: string, credential: PasskeyCredential): Promise<void> {
|
||||
const user = this.users.get(userId);
|
||||
if (!user) {
|
||||
throw new Error(`User ${userId} not found`);
|
||||
}
|
||||
|
||||
// Check for duplicate credential ID
|
||||
if (user.credentials.some(c => c.id === credential.id)) {
|
||||
throw new Error(`Credential already exists for user ${userId}`);
|
||||
}
|
||||
|
||||
user.credentials.push(credential);
|
||||
}
|
||||
|
||||
async getCredential(credentialId: string): Promise<PasskeyCredential | null> {
|
||||
for (const user of this.users.values()) {
|
||||
const cred = user.credentials.find(c => c.id === credentialId);
|
||||
if (cred) return cred;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async getCredentialsByUserId(userId: string): Promise<PasskeyCredential[]> {
|
||||
const user = this.users.get(userId);
|
||||
return user?.credentials ?? [];
|
||||
}
|
||||
|
||||
async updateCredentialCounter(credentialId: string, counter: number): Promise<void> {
|
||||
for (const user of this.users.values()) {
|
||||
const cred = user.credentials.find(c => c.id === credentialId);
|
||||
if (cred) {
|
||||
cred.counter = counter;
|
||||
cred.lastUsed = new Date().toISOString();
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new Error(`Credential ${credentialId} not found`);
|
||||
}
|
||||
|
||||
async deleteCredential(userId: string, credentialId: string): Promise<void> {
|
||||
const user = this.users.get(userId);
|
||||
if (!user) {
|
||||
throw new Error(`User ${userId} not found`);
|
||||
}
|
||||
|
||||
const idx = user.credentials.findIndex(c => c.id === credentialId);
|
||||
if (idx >= 0) {
|
||||
user.credentials.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user