Skip to content

Commit

Permalink
feat(clerk-js): Experimental CRUD methods for passkeys (#2926)
Browse files Browse the repository at this point in the history
  • Loading branch information
panteliselef authored Mar 5, 2024
1 parent c86f73b commit 008ac42
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 26 deletions.
12 changes: 12 additions & 0 deletions .changeset/wet-carpets-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@clerk/clerk-js': minor
'@clerk/types': minor
---

Experimental support for reading, updating, and deleting a user's registered passkeys.
- Get the user's passkeys
`clerk.user.__experimental__passkeys`
- Update the name of a passkey
`clerk.user.__experimental__passkeys?.[0].update({name:'work laptop passkey'})`
- Delete a passkey
`clerk.user.__experimental__passkeys?.[0].delete()`
45 changes: 23 additions & 22 deletions packages/clerk-js/src/core/resources/Passkey.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import type { PasskeyJSON, PasskeyResource, PasskeyVerificationResource } from '@clerk/types';
import type {
DeletedObjectJSON,
DeletedObjectResource,
PasskeyJSON,
PasskeyResource,
PasskeyVerificationResource,
UpdatePasskeyParams,
} from '@clerk/types';

import { unixEpochToDate } from '../../utils/date';
import type { PublicKeyCredentialWithAuthenticatorAttestationResponse } from '../../utils/passkeys';
Expand All @@ -7,7 +14,7 @@ import {
serializePublicKeyCredential,
webAuthnCreateCredential,
} from '../../utils/passkeys';
import { BaseResource, ClerkRuntimeError, PasskeyVerification } from './internal';
import { BaseResource, ClerkRuntimeError, DeletedObject, PasskeyVerification } from './internal';

export class Passkey extends BaseResource implements PasskeyResource {
id!: string;
Expand Down Expand Up @@ -43,13 +50,6 @@ export class Passkey extends BaseResource implements PasskeyResource {
}).then(res => new Passkey(res?.response as PasskeyJSON));
}

/**
* TODO-PASSKEYS: Implement this later
*
* GET /v1/me/passkeys
*/
static async get() {}

/**
* Developers should not be able to create a new Passkeys from an already instanced object
*/
Expand Down Expand Up @@ -92,25 +92,26 @@ export class Passkey extends BaseResource implements PasskeyResource {
}

/**
* TODO-PASSKEYS: Implement this later
*
* PATCH /v1/me/passkeys/{passkeyIdentificationID}
*/
update = (): Promise<PasskeyResource> => this._basePatch();
update = (params: UpdatePasskeyParams): Promise<PasskeyResource> =>
this._basePatch({
body: params,
});

/**
* TODO-PASSKEYS: Implement this later
*
* DELETE /v1/me/passkeys/{passkeyIdentificationID}
*/
destroy = (): Promise<void> => this._baseDelete();

/**
* TODO-PASSKEYS: Implement this later
*
* GET /v1/me/passkeys/{passkeyIdentificationID}
*/
reload = () => this._baseGet();
delete = async (): Promise<DeletedObjectResource> => {
const json = (
await BaseResource._fetch<DeletedObjectJSON>({
path: `${this.path()}/${this.id}`,
method: 'DELETE',
})
)?.response as unknown as DeletedObjectJSON;

return new DeletedObject(json);
};

protected fromJSON(data: PasskeyJSON | null): this {
if (!data) {
Expand Down
3 changes: 3 additions & 0 deletions packages/clerk-js/src/core/resources/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export class User extends BaseResource implements UserResource {
phoneNumbers: PhoneNumberResource[] = [];
web3Wallets: Web3WalletResource[] = [];
externalAccounts: ExternalAccountResource[] = [];
__experimental_passkeys: PasskeyResource[] = [];

samlAccounts: SamlAccountResource[] = [];

Expand Down Expand Up @@ -337,6 +338,8 @@ export class User extends BaseResource implements UserResource {
ea => new ExternalAccount(ea, this.path() + '/external_accounts'),
);

this.__experimental_passkeys = (data.passkeys || []).map(passkey => new Passkey(passkey));

this.organizationMemberships = (data.organization_memberships || []).map(om => new OrganizationMembership(om));

this.samlAccounts = (data.saml_accounts || []).map(sa => new SamlAccount(sa, this.path() + '/saml_accounts'));
Expand Down
1 change: 0 additions & 1 deletion packages/clerk-js/src/utils/passkeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ class Base64Converter {
}

static decode(base64url: string): ArrayBuffer {
// TODO-PASSKEYS: check if this can be replaced with Buffer.from(base64url, 'base64');
const base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');

const binaryString = atob(base64);
Expand Down
2 changes: 1 addition & 1 deletion packages/types/src/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ export interface UserJSON extends ClerkResourceJSON {
phone_numbers: PhoneNumberJSON[];
web3_wallets: Web3WalletJSON[];
external_accounts: ExternalAccountJSON[];

passkeys: PasskeyJSON[];
saml_accounts: SamlAccountJSON[];

organization_memberships: OrganizationMembershipJSON[];
Expand Down
10 changes: 9 additions & 1 deletion packages/types/src/passkey.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import type { DeletedObjectResource } from './deletedObject';
import type { PasskeyJSON } from './json';
import type { ClerkResource } from './resource';
import type { SnakeToCamel } from './utils';
import type { PasskeyVerificationResource } from './verification';

export interface PublicKeyOptions extends PublicKeyCredentialCreationOptions {}
type UpdatePasskeyJSON = Pick<PasskeyJSON, 'name'>;

export type UpdatePasskeyParams = Partial<SnakeToCamel<UpdatePasskeyJSON>>;

export interface PasskeyResource extends ClerkResource {
id: string;
Expand All @@ -11,4 +16,7 @@ export interface PasskeyResource extends ClerkResource {
lastUsedAt: Date | null;
updatedAt: Date;
createdAt: Date;

update: (params: UpdatePasskeyParams) => Promise<PasskeyResource>;
delete: () => Promise<DeletedObjectResource>;
}
6 changes: 5 additions & 1 deletion packages/types/src/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@ export interface UserResource extends ClerkResource {
phoneNumbers: PhoneNumberResource[];
web3Wallets: Web3WalletResource[];
externalAccounts: ExternalAccountResource[];

/**
* @experimental
* This property is experimental, avoid using this in production applications
*/
__experimental_passkeys: PasskeyResource[];
samlAccounts: SamlAccountResource[];

organizationMemberships: OrganizationMembershipResource[];
Expand Down

0 comments on commit 008ac42

Please sign in to comment.