@plainkey/browser 0.8.0 → 0.11.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/dist/index.d.ts CHANGED
@@ -22,17 +22,17 @@ declare class PlainKey {
22
22
  /**
23
23
  * Registration of a new user with a passkey. Will require user interaction to create a passkey.
24
24
  *
25
- * @param userName - A stable unique identifier for the user, like an email address or username.
25
+ * @param userName - A unique identifier for the user, like an email address or username.
26
26
  * Can be empty for usernameless authentication.
27
27
  */
28
28
  createUserWithPasskey(userName?: string): Promise<CreateUserWithPasskeyResult>;
29
29
  /**
30
30
  * Adds a passkey to an existing user. Will require user interaction to create a passkey.
31
31
  *
32
- * @param userToken - The user authentication token, is returned from .authenticate() and createUserWithPasskey().
32
+ * @param authenticationToken - The user authentication token, is returned from .authenticate() and createUserWithPasskey().
33
33
  * Do NOT store it in local storage, database, etc. Always keep it in memory.
34
34
  */
35
- addPasskey(userToken: string): Promise<AddPasskeyResult>;
35
+ addPasskey(authenticationToken: string): Promise<AddPasskeyResult>;
36
36
  /**
37
37
  * Authenticates a user. Can be used for login, verification, 2FA, etc.
38
38
  * Will require user interaction to authenticate.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/plainKey.ts"],"sourcesContent":[],"mappings":";;;;;;AAiCA;;;;;;AAyL+D,cAzLlD,QAAA,CAyLkD;EAAR,iBAAA,SAAA;EAAO,iBAAA,OAAA;;;;;;;;;;;;;4CAtIZ,QAAQ;;;;;;;iCAoEnB,QAAQ;;;;;;;;gCAkET,iBAAiB,QAAQ"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/plainKey.ts"],"sourcesContent":[],"mappings":";;;;;;AA8BA;;;;;;AAoL+D,cApLlD,QAAA,CAoLkD;EAAR,iBAAA,SAAA;EAAO,iBAAA,OAAA;;;;;;;;;;;;;4CAjIZ,QAAQ;;;;;;;2CAgET,QAAQ;;;;;;;;gCAiEnB,iBAAiB,QAAQ"}
package/dist/index.js CHANGED
@@ -43,25 +43,24 @@ var PlainKey = class {
43
43
  /**
44
44
  * Registration of a new user with a passkey. Will require user interaction to create a passkey.
45
45
  *
46
- * @param userName - A stable unique identifier for the user, like an email address or username.
46
+ * @param userName - A unique identifier for the user, like an email address or username.
47
47
  * Can be empty for usernameless authentication.
48
48
  */
49
49
  async createUserWithPasskey(userName) {
50
50
  try {
51
- const beginParams = { userName };
51
+ const beginRequestBody = { userName };
52
52
  const beginResponse = await fetch(`${this.baseUrl}/user/register/begin`, {
53
53
  method: "POST",
54
54
  headers: {
55
55
  "Content-Type": "application/json",
56
56
  "x-project-id": this.projectId
57
57
  },
58
- body: JSON.stringify(beginParams)
58
+ body: JSON.stringify(beginRequestBody)
59
59
  });
60
- const { options, user } = await this.parseResponse(beginResponse);
61
- const credential = await startRegistration({ optionsJSON: options });
62
- const completeParams = {
63
- userIdentifier: { userId: user.id },
64
- credential
60
+ const { userId, options } = await this.parseResponse(beginResponse);
61
+ const completeRequestBody = {
62
+ userId,
63
+ credential: await startRegistration({ optionsJSON: options })
65
64
  };
66
65
  const completeResponse = await fetch(`${this.baseUrl}/user/register/complete`, {
67
66
  method: "POST",
@@ -69,15 +68,15 @@ var PlainKey = class {
69
68
  "Content-Type": "application/json",
70
69
  "x-project-id": this.projectId
71
70
  },
72
- body: JSON.stringify(completeParams)
71
+ body: JSON.stringify(completeRequestBody)
73
72
  });
74
73
  const completeResponseData = await this.parseResponse(completeResponse);
75
74
  if (!completeResponseData.success) throw new Error("Server could not complete registration");
76
75
  return {
77
76
  success: completeResponseData.success,
78
77
  data: {
79
- user: completeResponseData.user,
80
- token: completeResponseData.token,
78
+ userId: completeResponseData.userId,
79
+ authenticationToken: completeResponseData.authenticationToken,
81
80
  credential: completeResponseData.credential
82
81
  }
83
82
  };
@@ -91,12 +90,12 @@ var PlainKey = class {
91
90
  /**
92
91
  * Adds a passkey to an existing user. Will require user interaction to create a passkey.
93
92
  *
94
- * @param userToken - The user authentication token, is returned from .authenticate() and createUserWithPasskey().
93
+ * @param authenticationToken - The user authentication token, is returned from .authenticate() and createUserWithPasskey().
95
94
  * Do NOT store it in local storage, database, etc. Always keep it in memory.
96
95
  */
97
- async addPasskey(userToken) {
96
+ async addPasskey(authenticationToken) {
98
97
  try {
99
- const beginParams = { userToken };
98
+ const beginParams = { authenticationToken };
100
99
  const beginResponse = await fetch(`${this.baseUrl}/user/credential/begin`, {
101
100
  method: "POST",
102
101
  headers: {
@@ -105,8 +104,11 @@ var PlainKey = class {
105
104
  },
106
105
  body: JSON.stringify(beginParams)
107
106
  });
108
- const { options, user } = await this.parseResponse(beginResponse);
109
- const completeParams = { credential: await startRegistration({ optionsJSON: options }) };
107
+ const { options } = await this.parseResponse(beginResponse);
108
+ const completeParams = {
109
+ authenticationToken,
110
+ credential: await startRegistration({ optionsJSON: options })
111
+ };
110
112
  const completeResponse = await fetch(`${this.baseUrl}/user/credential/complete`, {
111
113
  method: "POST",
112
114
  headers: {
@@ -120,8 +122,7 @@ var PlainKey = class {
120
122
  return {
121
123
  success: completeResponseData.success,
122
124
  data: {
123
- user: completeResponseData.user,
124
- token: completeResponseData.token,
125
+ authenticationToken: completeResponseData.authenticationToken,
125
126
  credential: completeResponseData.credential
126
127
  }
127
128
  };
@@ -158,7 +159,7 @@ var PlainKey = class {
158
159
  loginSessionId: beginResponseData.loginSession.id,
159
160
  authenticationResponse
160
161
  };
161
- const verificationResponse = await fetch(`${this.baseUrl}/authenticate/complete`, {
162
+ const authenticateCompleteResponse = await fetch(`${this.baseUrl}/authenticate/complete`, {
162
163
  method: "POST",
163
164
  headers: {
164
165
  "Content-Type": "application/json",
@@ -166,14 +167,9 @@ var PlainKey = class {
166
167
  },
167
168
  body: JSON.stringify(completeParams)
168
169
  });
169
- const verificationResponseData = await this.parseResponse(verificationResponse);
170
- if (!verificationResponseData.verified) throw new Error("Server could not verify authentication");
171
170
  return {
172
- success: verificationResponseData.verified,
173
- data: {
174
- user: verificationResponseData.user,
175
- token: verificationResponseData.token
176
- }
171
+ success: true,
172
+ data: { authenticationToken: (await this.parseResponse(authenticateCompleteResponse)).authenticationToken }
177
173
  };
178
174
  } catch (error) {
179
175
  return {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["bodyText: string","json: any","beginParams: RegistrationBeginRequest","credential: RegistrationResponseJSON","completeParams: RegistrationCompleteRequest","completeResponseData: RegistrationCompleteResponse","beginParams: UserCredentialBeginRequest","completeParams: UserCredentialCompleteRequest","completeResponseData: UserCredentialCompleteResponse","beginParams: LoginBeginRequest","beginResponseData: LoginBeginResponse","authenticationResponse: AuthenticationResponseJSON","completeParams: LoginCompleteRequest","verificationResponseData: LoginCompleteResponse"],"sources":["../src/plainKey.ts"],"sourcesContent":["import { startAuthentication, startRegistration } from \"@simplewebauthn/browser\"\nimport { RegistrationResponseJSON, AuthenticationResponseJSON } from \"@simplewebauthn/browser\"\n\nimport type {\n RegistrationBeginRequest,\n RegistrationCompleteRequest,\n UserCredentialBeginRequest,\n UserCredentialCompleteRequest,\n LoginBeginRequest,\n LoginCompleteRequest,\n UserIdentifier,\n CreateUserWithPasskeyResult,\n AddPasskeyResult,\n AuthenticateResult\n} from \"@plainkey/types\"\n\nimport type {\n RegistrationBeginResponse,\n RegistrationCompleteResponse,\n UserCredentialBeginResponse,\n UserCredentialCompleteResponse,\n LoginBeginResponse,\n LoginCompleteResponse\n} from \"@plainkey/types\"\n\n/**\n * PlainKey client for the browser. Used to register new users, add passkeys to existing users, and log users in.\n *\n * Docs: https://plainkey.io/docs\n *\n * @param projectId - Your PlainKey project ID. You can find it in the PlainKey admin dashboard.\n * @param baseUrl - Set by default to https://api.plainkey.io/browser. Change only for development purposes.\n */\nexport class PlainKey {\n private readonly projectId: string\n private readonly baseUrl: string\n\n constructor(projectId: string, baseUrl: string = \"https://api.plainkey.io/browser\") {\n if (!projectId) throw new Error(\"Project ID is required\")\n if (!baseUrl) throw new Error(\"Base URL is required\")\n\n this.projectId = projectId\n this.baseUrl = baseUrl.replace(/\\/$/, \"\") // Remove trailing slash\n }\n\n /**\n * Helper to parse response JSON.\n * Throws error if status code is not 200 OK, if the response is not valid JSON.\n */\n private async parseResponse<T = any>(response: Response): Promise<T> {\n let bodyText: string\n\n // Read as text first to avoid JSON.parse errors on any HTML/plaintext error responses.\n try {\n bodyText = await response.text()\n } catch {\n throw new Error(\"Network error while reading server response\")\n }\n\n // Parse the response text as JSON.\n let json: any\n\n try {\n json = bodyText ? JSON.parse(bodyText) : {}\n } catch {\n if (!response.ok) throw new Error(\"Server returned an invalid JSON error response\")\n throw new Error(\"Invalid JSON received from server\")\n }\n\n if (!response.ok) {\n // Server should return { error: string }\n const message = json && typeof json.error === \"string\" ? json.error : \"Unknown server error\"\n throw new Error(message)\n }\n\n return json as T\n }\n\n /**\n * Registration of a new user with a passkey. Will require user interaction to create a passkey.\n *\n * @param userName - A stable unique identifier for the user, like an email address or username.\n * Can be empty for usernameless authentication.\n */\n async createUserWithPasskey(userName?: string): Promise<CreateUserWithPasskeyResult> {\n try {\n // Step 1: Get registration options from server\n const beginParams: RegistrationBeginRequest = { userName }\n const beginResponse = await fetch(`${this.baseUrl}/user/register/begin`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-project-id\": this.projectId\n },\n body: JSON.stringify(beginParams)\n })\n\n // Parse response JSON\n const { options, user } = await this.parseResponse<RegistrationBeginResponse>(beginResponse)\n\n // Step 2: Create credential using browser's WebAuthn API\n const credential: RegistrationResponseJSON = await startRegistration({\n optionsJSON: options\n })\n\n // Step 3: Send credential to server for verification\n const completeParams: RegistrationCompleteRequest = {\n userIdentifier: { userId: user.id },\n credential\n }\n\n const completeResponse = await fetch(`${this.baseUrl}/user/register/complete`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-project-id\": this.projectId\n },\n body: JSON.stringify(completeParams)\n })\n\n // Parse response JSON\n const completeResponseData: RegistrationCompleteResponse =\n await this.parseResponse<RegistrationCompleteResponse>(completeResponse)\n\n if (!completeResponseData.success) throw new Error(\"Server could not complete registration\")\n\n // Return success\n return {\n success: completeResponseData.success,\n data: {\n user: completeResponseData.user,\n token: completeResponseData.token,\n credential: completeResponseData.credential\n }\n }\n } catch (error) {\n // Return error\n return {\n success: false,\n error: {\n message: error instanceof Error ? error.message : \"Unknown error\"\n }\n }\n }\n }\n\n /**\n * Adds a passkey to an existing user. Will require user interaction to create a passkey.\n *\n * @param userToken - The user authentication token, is returned from .authenticate() and createUserWithPasskey().\n * Do NOT store it in local storage, database, etc. Always keep it in memory.\n */\n async addPasskey(userToken: string): Promise<AddPasskeyResult> {\n try {\n // Step 1: Get credential registration options from server\n const beginParams: UserCredentialBeginRequest = { userToken }\n const beginResponse = await fetch(`${this.baseUrl}/user/credential/begin`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-project-id\": this.projectId\n },\n body: JSON.stringify(beginParams)\n })\n\n // Parse response JSON\n const { options, user }: UserCredentialBeginResponse =\n await this.parseResponse<UserCredentialBeginResponse>(beginResponse)\n\n // Step 2: Create credential using browser's WebAuthn API\n const credential: RegistrationResponseJSON = await startRegistration({ optionsJSON: options })\n\n // Step 3: Send credential to server for verification\n const completeParams: UserCredentialCompleteRequest = { credential }\n\n const completeResponse = await fetch(`${this.baseUrl}/user/credential/complete`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-project-id\": this.projectId\n },\n body: JSON.stringify(completeParams)\n })\n\n // Parse response JSON\n const completeResponseData: UserCredentialCompleteResponse =\n await this.parseResponse<UserCredentialCompleteResponse>(completeResponse)\n\n if (!completeResponseData.success)\n throw new Error(\"Server could not complete passkey registration\")\n\n // Return success\n return {\n success: completeResponseData.success,\n data: {\n user: completeResponseData.user,\n token: completeResponseData.token,\n credential: completeResponseData.credential\n }\n }\n } catch (error) {\n // Return error\n return {\n success: false,\n error: {\n message: error instanceof Error ? error.message : \"Unknown error\"\n }\n }\n }\n }\n\n /**\n * Authenticates a user. Can be used for login, verification, 2FA, etc.\n * Will require user interaction to authenticate.\n *\n * @param userIdentifier - Optional object containing either the user's PlainKey User ID or their userName.\n * Does not have to be provided for usernameless authentication.\n */\n async authenticate(userIdentifier?: UserIdentifier): Promise<AuthenticateResult> {\n try {\n // Step 1: Get authentication options from server\n const beginParams: LoginBeginRequest = { userIdentifier }\n const beginResponse = await fetch(`${this.baseUrl}/authenticate/begin`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-project-id\": this.projectId\n },\n body: JSON.stringify(beginParams)\n })\n\n // Parse response JSON\n const beginResponseData: LoginBeginResponse =\n await this.parseResponse<LoginBeginResponse>(beginResponse)\n\n if (!beginResponseData.options)\n throw new Error(\"Server returned no options in login begin response\")\n\n // Step 2: Pass options to the authenticator and wait for response\n const authenticationResponse: AuthenticationResponseJSON = await startAuthentication({\n optionsJSON: beginResponseData.options\n })\n\n if (!authenticationResponse) throw new Error(\"No authentication response from browser\")\n\n // Step 3: POST the response to the server\n // This uses the authentication session ID from the begin response - always in JS memory.\n // Do not store it in local storage, database, etc.\n const completeParams: LoginCompleteRequest = {\n loginSessionId: beginResponseData.loginSession.id,\n authenticationResponse\n }\n\n const verificationResponse = await fetch(`${this.baseUrl}/authenticate/complete`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-project-id\": this.projectId\n },\n body: JSON.stringify(completeParams)\n })\n\n const verificationResponseData: LoginCompleteResponse =\n await this.parseResponse<LoginCompleteResponse>(verificationResponse)\n\n if (!verificationResponseData.verified)\n throw new Error(\"Server could not verify authentication\")\n\n // Return success\n return {\n success: verificationResponseData.verified,\n data: {\n user: verificationResponseData.user,\n token: verificationResponseData.token\n }\n }\n } catch (error) {\n // Return error\n return {\n success: false,\n error: {\n message: error instanceof Error ? error.message : \"Unknown error\"\n }\n }\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["bodyText: string","json: any","beginRequestBody: UserRegisterBeginRequest","completeRequestBody: UserRegisterCompleteRequest","completeResponseData: UserRegisterCompleteResponse","beginParams: UserCredentialBeginRequest","completeParams: UserCredentialCompleteRequest","completeResponseData: UserCredentialCompleteResponse","beginParams: LoginBeginRequest","beginResponseData: AuthenticationBeginResponse","authenticationResponse: AuthenticationResponseJSON","completeParams: LoginCompleteRequest"],"sources":["../src/plainKey.ts"],"sourcesContent":["import { startAuthentication, startRegistration } from \"@simplewebauthn/browser\"\nimport { RegistrationResponseJSON, AuthenticationResponseJSON } from \"@simplewebauthn/browser\"\n\nimport type {\n UserCredentialBeginRequest,\n UserCredentialCompleteRequest,\n LoginBeginRequest,\n LoginCompleteRequest,\n UserIdentifier,\n CreateUserWithPasskeyResult,\n AddPasskeyResult,\n AuthenticateResult,\n UserRegisterBeginRequest,\n UserRegisterBeginResponse,\n UserRegisterCompleteRequest,\n UserRegisterCompleteResponse,\n AuthenticationCompleteResponse,\n AuthenticationBeginResponse\n} from \"@plainkey/types\"\n\nimport type { UserCredentialBeginResponse, UserCredentialCompleteResponse } from \"@plainkey/types\"\n\n/**\n * PlainKey client for the browser. Used to register new users, add passkeys to existing users, and log users in.\n *\n * Docs: https://plainkey.io/docs\n *\n * @param projectId - Your PlainKey project ID. You can find it in the PlainKey admin dashboard.\n * @param baseUrl - Set by default to https://api.plainkey.io/browser. Change only for development purposes.\n */\nexport class PlainKey {\n private readonly projectId: string\n private readonly baseUrl: string\n\n constructor(projectId: string, baseUrl: string = \"https://api.plainkey.io/browser\") {\n if (!projectId) throw new Error(\"Project ID is required\")\n if (!baseUrl) throw new Error(\"Base URL is required\")\n\n this.projectId = projectId\n this.baseUrl = baseUrl.replace(/\\/$/, \"\") // Remove trailing slash\n }\n\n /**\n * Helper to parse response JSON.\n * Throws error if status code is not 200 OK, if the response is not valid JSON.\n */\n private async parseResponse<T = any>(response: Response): Promise<T> {\n let bodyText: string\n\n // Read as text first to avoid JSON.parse errors on any HTML/plaintext error responses.\n try {\n bodyText = await response.text()\n } catch {\n throw new Error(\"Network error while reading server response\")\n }\n\n // Parse the response text as JSON.\n let json: any\n\n try {\n json = bodyText ? JSON.parse(bodyText) : {}\n } catch {\n if (!response.ok) throw new Error(\"Server returned an invalid JSON error response\")\n throw new Error(\"Invalid JSON received from server\")\n }\n\n if (!response.ok) {\n // Server should return { error: string }\n const message = json && typeof json.error === \"string\" ? json.error : \"Unknown server error\"\n throw new Error(message)\n }\n\n return json as T\n }\n\n /**\n * Registration of a new user with a passkey. Will require user interaction to create a passkey.\n *\n * @param userName - A unique identifier for the user, like an email address or username.\n * Can be empty for usernameless authentication.\n */\n async createUserWithPasskey(userName?: string): Promise<CreateUserWithPasskeyResult> {\n try {\n // Step 1: Get registration options from server\n const beginRequestBody: UserRegisterBeginRequest = { userName }\n const beginResponse = await fetch(`${this.baseUrl}/user/register/begin`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-project-id\": this.projectId\n },\n body: JSON.stringify(beginRequestBody)\n })\n\n // Parse response JSON\n const { userId, options } = await this.parseResponse<UserRegisterBeginResponse>(beginResponse)\n\n // Step 2: Create credential using browser's WebAuthn API\n const credential: RegistrationResponseJSON = await startRegistration({\n optionsJSON: options\n })\n\n // Step 3: Send credential to server for verification\n const completeRequestBody: UserRegisterCompleteRequest = { userId, credential }\n const completeResponse = await fetch(`${this.baseUrl}/user/register/complete`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-project-id\": this.projectId\n },\n body: JSON.stringify(completeRequestBody)\n })\n\n // Parse response JSON\n const completeResponseData: UserRegisterCompleteResponse =\n await this.parseResponse<UserRegisterCompleteResponse>(completeResponse)\n\n if (!completeResponseData.success) throw new Error(\"Server could not complete registration\")\n\n // Return success\n return {\n success: completeResponseData.success,\n data: {\n userId: completeResponseData.userId,\n authenticationToken: completeResponseData.authenticationToken,\n credential: completeResponseData.credential\n }\n }\n } catch (error) {\n // Return error\n return {\n success: false,\n error: {\n message: error instanceof Error ? error.message : \"Unknown error\"\n }\n }\n }\n }\n\n /**\n * Adds a passkey to an existing user. Will require user interaction to create a passkey.\n *\n * @param authenticationToken - The user authentication token, is returned from .authenticate() and createUserWithPasskey().\n * Do NOT store it in local storage, database, etc. Always keep it in memory.\n */\n async addPasskey(authenticationToken: string): Promise<AddPasskeyResult> {\n try {\n // Step 1: Get credential registration options from server\n const beginParams: UserCredentialBeginRequest = { authenticationToken }\n const beginResponse = await fetch(`${this.baseUrl}/user/credential/begin`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-project-id\": this.projectId\n },\n body: JSON.stringify(beginParams)\n })\n\n // Parse response JSON\n const { options }: UserCredentialBeginResponse =\n await this.parseResponse<UserCredentialBeginResponse>(beginResponse)\n\n // Step 2: Create credential using browser's WebAuthn API\n const credential: RegistrationResponseJSON = await startRegistration({ optionsJSON: options })\n\n // Step 3: Send credential to server for verification\n const completeParams: UserCredentialCompleteRequest = { authenticationToken, credential }\n\n const completeResponse = await fetch(`${this.baseUrl}/user/credential/complete`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-project-id\": this.projectId\n },\n body: JSON.stringify(completeParams)\n })\n\n // Parse response JSON\n const completeResponseData: UserCredentialCompleteResponse =\n await this.parseResponse<UserCredentialCompleteResponse>(completeResponse)\n\n if (!completeResponseData.success)\n throw new Error(\"Server could not complete passkey registration\")\n\n // Return success\n return {\n success: completeResponseData.success,\n data: {\n authenticationToken: completeResponseData.authenticationToken,\n credential: completeResponseData.credential\n }\n }\n } catch (error) {\n // Return error\n return {\n success: false,\n error: {\n message: error instanceof Error ? error.message : \"Unknown error\"\n }\n }\n }\n }\n\n /**\n * Authenticates a user. Can be used for login, verification, 2FA, etc.\n * Will require user interaction to authenticate.\n *\n * @param userIdentifier - Optional object containing either the user's PlainKey User ID or their userName.\n * Does not have to be provided for usernameless authentication.\n */\n async authenticate(userIdentifier?: UserIdentifier): Promise<AuthenticateResult> {\n try {\n // Step 1: Get authentication options from server\n const beginParams: LoginBeginRequest = { userIdentifier }\n const beginResponse = await fetch(`${this.baseUrl}/authenticate/begin`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-project-id\": this.projectId\n },\n body: JSON.stringify(beginParams)\n })\n\n // Parse response JSON\n const beginResponseData: AuthenticationBeginResponse =\n await this.parseResponse<AuthenticationBeginResponse>(beginResponse)\n\n if (!beginResponseData.options)\n throw new Error(\"Server returned no options in login begin response\")\n\n // Step 2: Pass options to the authenticator and wait for response\n const authenticationResponse: AuthenticationResponseJSON = await startAuthentication({\n optionsJSON: beginResponseData.options\n })\n\n if (!authenticationResponse) throw new Error(\"No authentication response from browser\")\n\n // Step 3: POST the response to the server\n // This uses the authentication session ID from the begin response - always in JS memory.\n // Do not store it in local storage, database, etc.\n const completeParams: LoginCompleteRequest = {\n loginSessionId: beginResponseData.loginSession.id,\n authenticationResponse\n }\n\n const authenticateCompleteResponse = await fetch(`${this.baseUrl}/authenticate/complete`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-project-id\": this.projectId\n },\n body: JSON.stringify(completeParams)\n })\n\n const authCompleteResponseData: AuthenticationCompleteResponse =\n await this.parseResponse<AuthenticationCompleteResponse>(authenticateCompleteResponse)\n\n // Return success\n return {\n success: true,\n data: {\n authenticationToken: authCompleteResponseData.authenticationToken\n }\n }\n } catch (error) {\n // Return error\n return {\n success: false,\n error: {\n message: error instanceof Error ? error.message : \"Unknown error\"\n }\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;AA8BA,IAAa,WAAb,MAAsB;CAIpB,YAAY,WAAmB,UAAkB,mCAAmC;AAClF,MAAI,CAAC,UAAW,OAAM,IAAI,MAAM,yBAAyB;AACzD,MAAI,CAAC,QAAS,OAAM,IAAI,MAAM,uBAAuB;AAErD,OAAK,YAAY;AACjB,OAAK,UAAU,QAAQ,QAAQ,OAAO,GAAG;;;;;;CAO3C,MAAc,cAAuB,UAAgC;EACnE,IAAIA;AAGJ,MAAI;AACF,cAAW,MAAM,SAAS,MAAM;UAC1B;AACN,SAAM,IAAI,MAAM,8CAA8C;;EAIhE,IAAIC;AAEJ,MAAI;AACF,UAAO,WAAW,KAAK,MAAM,SAAS,GAAG,EAAE;UACrC;AACN,OAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,iDAAiD;AACnF,SAAM,IAAI,MAAM,oCAAoC;;AAGtD,MAAI,CAAC,SAAS,IAAI;GAEhB,MAAM,UAAU,QAAQ,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;AACtE,SAAM,IAAI,MAAM,QAAQ;;AAG1B,SAAO;;;;;;;;CAST,MAAM,sBAAsB,UAAyD;AACnF,MAAI;GAEF,MAAMC,mBAA6C,EAAE,UAAU;GAC/D,MAAM,gBAAgB,MAAM,MAAM,GAAG,KAAK,QAAQ,uBAAuB;IACvE,QAAQ;IACR,SAAS;KACP,gBAAgB;KAChB,gBAAgB,KAAK;KACtB;IACD,MAAM,KAAK,UAAU,iBAAiB;IACvC,CAAC;GAGF,MAAM,EAAE,QAAQ,YAAY,MAAM,KAAK,cAAyC,cAAc;GAQ9F,MAAMC,sBAAmD;IAAE;IAAQ,YALtB,MAAM,kBAAkB,EACnE,aAAa,SACd,CAAC;IAG6E;GAC/E,MAAM,mBAAmB,MAAM,MAAM,GAAG,KAAK,QAAQ,0BAA0B;IAC7E,QAAQ;IACR,SAAS;KACP,gBAAgB;KAChB,gBAAgB,KAAK;KACtB;IACD,MAAM,KAAK,UAAU,oBAAoB;IAC1C,CAAC;GAGF,MAAMC,uBACJ,MAAM,KAAK,cAA4C,iBAAiB;AAE1E,OAAI,CAAC,qBAAqB,QAAS,OAAM,IAAI,MAAM,yCAAyC;AAG5F,UAAO;IACL,SAAS,qBAAqB;IAC9B,MAAM;KACJ,QAAQ,qBAAqB;KAC7B,qBAAqB,qBAAqB;KAC1C,YAAY,qBAAqB;KAClC;IACF;WACM,OAAO;AAEd,UAAO;IACL,SAAS;IACT,OAAO,EACL,SAAS,iBAAiB,QAAQ,MAAM,UAAU,iBACnD;IACF;;;;;;;;;CAUL,MAAM,WAAW,qBAAwD;AACvE,MAAI;GAEF,MAAMC,cAA0C,EAAE,qBAAqB;GACvE,MAAM,gBAAgB,MAAM,MAAM,GAAG,KAAK,QAAQ,yBAAyB;IACzE,QAAQ;IACR,SAAS;KACP,gBAAgB;KAChB,gBAAgB,KAAK;KACtB;IACD,MAAM,KAAK,UAAU,YAAY;IAClC,CAAC;GAGF,MAAM,EAAE,YACN,MAAM,KAAK,cAA2C,cAAc;GAMtE,MAAMC,iBAAgD;IAAE;IAAqB,YAHhC,MAAM,kBAAkB,EAAE,aAAa,SAAS,CAAC;IAGL;GAEzF,MAAM,mBAAmB,MAAM,MAAM,GAAG,KAAK,QAAQ,4BAA4B;IAC/E,QAAQ;IACR,SAAS;KACP,gBAAgB;KAChB,gBAAgB,KAAK;KACtB;IACD,MAAM,KAAK,UAAU,eAAe;IACrC,CAAC;GAGF,MAAMC,uBACJ,MAAM,KAAK,cAA8C,iBAAiB;AAE5E,OAAI,CAAC,qBAAqB,QACxB,OAAM,IAAI,MAAM,iDAAiD;AAGnE,UAAO;IACL,SAAS,qBAAqB;IAC9B,MAAM;KACJ,qBAAqB,qBAAqB;KAC1C,YAAY,qBAAqB;KAClC;IACF;WACM,OAAO;AAEd,UAAO;IACL,SAAS;IACT,OAAO,EACL,SAAS,iBAAiB,QAAQ,MAAM,UAAU,iBACnD;IACF;;;;;;;;;;CAWL,MAAM,aAAa,gBAA8D;AAC/E,MAAI;GAEF,MAAMC,cAAiC,EAAE,gBAAgB;GACzD,MAAM,gBAAgB,MAAM,MAAM,GAAG,KAAK,QAAQ,sBAAsB;IACtE,QAAQ;IACR,SAAS;KACP,gBAAgB;KAChB,gBAAgB,KAAK;KACtB;IACD,MAAM,KAAK,UAAU,YAAY;IAClC,CAAC;GAGF,MAAMC,oBACJ,MAAM,KAAK,cAA2C,cAAc;AAEtE,OAAI,CAAC,kBAAkB,QACrB,OAAM,IAAI,MAAM,qDAAqD;GAGvE,MAAMC,yBAAqD,MAAM,oBAAoB,EACnF,aAAa,kBAAkB,SAChC,CAAC;AAEF,OAAI,CAAC,uBAAwB,OAAM,IAAI,MAAM,0CAA0C;GAKvF,MAAMC,iBAAuC;IAC3C,gBAAgB,kBAAkB,aAAa;IAC/C;IACD;GAED,MAAM,+BAA+B,MAAM,MAAM,GAAG,KAAK,QAAQ,yBAAyB;IACxF,QAAQ;IACR,SAAS;KACP,gBAAgB;KAChB,gBAAgB,KAAK;KACtB;IACD,MAAM,KAAK,UAAU,eAAe;IACrC,CAAC;AAMF,UAAO;IACL,SAAS;IACT,MAAM,EACJ,sBANF,MAAM,KAAK,cAA8C,6BAA6B,EAMtC,qBAC/C;IACF;WACM,OAAO;AAEd,UAAO;IACL,SAAS;IACT,OAAO,EACL,SAAS,iBAAiB,QAAQ,MAAM,UAAU,iBACnD;IACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plainkey/browser",
3
- "version": "0.8.0",
3
+ "version": "0.11.0",
4
4
  "description": "PlainKey browser SDK for passkey registration, login and credential management",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -23,6 +23,7 @@
23
23
  "access": "public"
24
24
  },
25
25
  "dependencies": {
26
+ "@plainkey/types": "0.11.0",
26
27
  "@simplewebauthn/browser": "^13.2.0"
27
28
  },
28
29
  "devDependencies": {