Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
678cf40
Implement passkey login hook and call it when the contact-info compon…
hajinsuha1 Jan 19, 2026
0043eaf
Add finishWebauthnAuthentication to usePasskeyLogin hook and enhance …
hajinsuha1 Jan 19, 2026
b6ea2d4
add unit tests and rename to loginWithPasskey
hajinsuha1 Jan 20, 2026
dfbd5bb
rename startPasskeyLogin to loginWithPasskey in contact-info
hajinsuha1 Jan 20, 2026
74d3679
Merge branch 'feature/webauthn-login' into W-20224267-passkey-login-i…
hajinsuha1 Jan 20, 2026
aff8cd5
use toJSON to encode credential and fallback to manual encoded on error
hajinsuha1 Jan 21, 2026
ba1f2d0
Update ESLint configuration to use latest ECMAScript version and adju…
hajinsuha1 Jan 21, 2026
4be0858
add unit test for fallback to manual encoding
hajinsuha1 Jan 21, 2026
baeeeb5
remove window.PublicKeyCredential call now that eslint version has be…
hajinsuha1 Jan 21, 2026
8513ed3
ensure errors are displayed when loginWithPasskey fails
hajinsuha1 Jan 22, 2026
03887c1
update eslint and remove es2025
hajinsuha1 Jan 22, 2026
8130e6e
increase bundle size
hajinsuha1 Jan 23, 2026
2cee2cf
Update ESLint configuration to support ECMAScript 2024
hajinsuha1 Jan 23, 2026
5ed8117
revert eslint changes and use /* global PublicKeyCredential */
hajinsuha1 Jan 23, 2026
17fa736
update unit tests
hajinsuha1 Jan 23, 2026
d9c5324
revert unnecessary changes to bundle size and eslint
hajinsuha1 Jan 23, 2026
d2c4c05
Handle NotAllowedError in usePasskeyLogin hook and add corresponding …
hajinsuha1 Jan 23, 2026
c52520f
Refactor contact info handling to handle merge basket when the custom…
hajinsuha1 Jan 26, 2026
1b88a0e
Test commit
hajinsuha1 Jan 26, 2026
02cc12b
Revert "Test commit"
hajinsuha1 Jan 26, 2026
c2cbe12
Enhance usePasskeyLogin hook to include usid and update corresponding…
hajinsuha1 Jan 26, 2026
7c599ea
Update contact info component to merge basket after successful login …
hajinsuha1 Jan 26, 2026
0176e46
Update bundle size limits in package.json for main.js and vendor.js
hajinsuha1 Jan 26, 2026
051f1a0
Refactor Webauthn unit tests to use specific types for input and expe…
hajinsuha1 Jan 26, 2026
5a59bac
Update contact info component to ensure basket is merged after passke…
hajinsuha1 Jan 26, 2026
54d95fc
Update contact info component to call handleMergeBasket after passkey…
hajinsuha1 Jan 27, 2026
2c8c8dc
Refactor usage of PublicKeyCredential in authentication hooks and log…
hajinsuha1 Jan 27, 2026
68da030
lint
hajinsuha1 Jan 27, 2026
c394c40
Merge branch 'feature/webauthn-login' into W-20224267-passkey-login-i…
hajinsuha1 Jan 27, 2026
e919f5f
Update copyright year for new files
hajinsuha1 Jan 28, 2026
7509729
Refactor passkey registration and login to utilize shared utility for…
hajinsuha1 Jan 28, 2026
238934c
Merge branch 'W-20224267-passkey-login-in-checkout' of https://github…
hajinsuha1 Jan 28, 2026
0d7f07b
update bundle size
hajinsuha1 Jan 28, 2026
2e04a99
Remove ts-ignore for resetPassword method now that commerce-sdk-isomo…
hajinsuha1 Jan 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
329 changes: 242 additions & 87 deletions packages/commerce-sdk-react/src/auth/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1305,113 +1305,268 @@ describe('Webauthn', () => {
} as ShopperLoginTypes.AuthenticatorAssertionResponseJson
}

test('authorizeWebauthnRegistration', async () => {
const auth = new Auth(config)
await auth.authorizeWebauthnRegistration({
user_id: 'test-user-id',
mode: 'test-mode',
channel_id: 'test-channel-id'
})

expect((auth as any).client.authorizeWebauthnRegistration).toHaveBeenCalledWith({
headers: {
Authorization: ''
test.each([
[
'with all parameters specified',
{
user_id: 'user@example.com',
mode: 'email',
channel_id: 'custom-channel-id',
client_id: 'custom-client-id',
locale: 'en-GB',
code_challenge: 'test-code-challenge',
callback_uri: 'https://example.com/callback',
idp_name: 'customIdp',
hint: 'custom_hint'
},
body: {
user_id: 'test-user-id',
mode: 'test-mode',
channel_id: 'test-channel-id'
{
user_id: 'user@example.com',
mode: 'email',
channel_id: 'custom-channel-id',
client_id: 'custom-client-id',
locale: 'en-GB',
code_challenge: 'test-code-challenge',
callback_uri: 'https://example.com/callback',
idp_name: 'customIdp',
hint: 'custom_hint'
}
})
})
],
[
'defaults optional parameters when only required parameters are specified',
{
user_id: 'user@example.com',
mode: 'email'
},
{
user_id: 'user@example.com',
mode: 'email',
channel_id: config.siteId
}
]
])(
'authorizeWebauthnRegistration %s',
async (
_,
input: Partial<ShopperLoginTypes.authorizeWebauthnRegistrationBodyType>,
expectedBody: Partial<ShopperLoginTypes.authorizeWebauthnRegistrationBodyType>
) => {
const auth = new Auth(config)
await auth.authorizeWebauthnRegistration(
input as ShopperLoginTypes.authorizeWebauthnRegistrationBodyType
)

test('startWebauthnUserRegistration', async () => {
const auth = new Auth(config)
await auth.startWebauthnUserRegistration({
channel_id: 'test-channel-id',
display_name: 'test-display-name',
nick_name: 'test-nick-name',
client_id: 'test-client-id',
pwd_action_token: 'test-pwd-action-token',
user_id: 'test-user-id'
})
expect((auth as any).client.authorizeWebauthnRegistration).toHaveBeenCalledWith({
headers: {
Authorization: ''
},
body: expectedBody
})
}
)

expect((auth as any).client.startWebauthnUserRegistration).toHaveBeenCalledWith({
headers: {
Authorization: ''
test.each([
[
'with all parameters specified',
{
user_id: 'user@example.com',
pwd_action_token: 'test-pwd-action-token',
channel_id: 'custom-channel-id',
client_id: 'custom-client-id',
display_name: 'Test Display Name',
nick_name: 'Test Nick Name'
},
body: {
display_name: 'test-display-name',
nick_name: 'test-nick-name',
client_id: 'test-client-id',
channel_id: 'test-channel-id',
{
user_id: 'user@example.com',
pwd_action_token: 'test-pwd-action-token',
user_id: 'test-user-id'
channel_id: 'custom-channel-id',
client_id: 'custom-client-id',
display_name: 'Test Display Name',
nick_name: 'Test Nick Name'
}
})
})
],
[
'defaults optional parameters when only required parameters are specified',
{
user_id: 'user@example.com',
pwd_action_token: 'test-pwd-action-token'
},
{
user_id: 'user@example.com',
pwd_action_token: 'test-pwd-action-token',
channel_id: config.siteId
}
]
])(
'startWebauthnUserRegistration %s',
async (
_,
input: Partial<ShopperLoginTypes.startWebauthnUserRegistrationBodyType>,
expectedBody: Partial<ShopperLoginTypes.startWebauthnUserRegistrationBodyType>
) => {
const auth = new Auth(config)
await auth.startWebauthnUserRegistration(
input as ShopperLoginTypes.startWebauthnUserRegistrationBodyType
)

test('finishWebauthnUserRegistration', async () => {
const auth = new Auth(config)
await auth.finishWebauthnUserRegistration({
client_id: 'test-client-id',
username: 'test-username',
credential: PUBLIC_KEY_CREDENTIAL_JSON,
channel_id: 'test-channel-id',
pwd_action_token: 'test-pwd-action-token'
})
expect((auth as any).client.startWebauthnUserRegistration).toHaveBeenCalledWith({
headers: {
Authorization: ''
},
body: expectedBody
})
}
)

expect((auth as any).client.finishWebauthnUserRegistration).toHaveBeenCalledWith({
headers: {
Authorization: ''
test.each([
[
'with all parameters specified',
{
username: 'user@example.com',
credential: PUBLIC_KEY_CREDENTIAL_JSON,
pwd_action_token: 'test-pwd-action-token',
channel_id: 'custom-channel-id',
client_id: 'custom-client-id'
},
body: {
client_id: 'test-client-id',
username: 'test-username',
{
username: 'user@example.com',
credential: PUBLIC_KEY_CREDENTIAL_JSON,
pwd_action_token: 'test-pwd-action-token',
channel_id: 'custom-channel-id',
client_id: 'custom-client-id'
}
],
[
'defaults optional parameters when only required parameters are specified',
{
username: 'user@example.com',
credential: PUBLIC_KEY_CREDENTIAL_JSON,
channel_id: 'test-channel-id',
pwd_action_token: 'test-pwd-action-token'
},
{
username: 'user@example.com',
credential: PUBLIC_KEY_CREDENTIAL_JSON,
pwd_action_token: 'test-pwd-action-token',
channel_id: config.siteId,
client_id: config.clientId
}
})
})
]
])(
'finishWebauthnUserRegistration %s',
async (
_,
input: Partial<ShopperLoginTypes.RegistrationFinishRequest>,
expectedBody: Partial<ShopperLoginTypes.RegistrationFinishRequest>
) => {
const auth = new Auth(config)
await auth.finishWebauthnUserRegistration(
input as ShopperLoginTypes.RegistrationFinishRequest
)

test('startWebauthnAuthentication', async () => {
const auth = new Auth(config)
await auth.startWebauthnAuthentication({
user_id: 'test-user-id',
channel_id: 'test-channel-id',
client_id: 'test-client-id'
})
expect((auth as any).client.finishWebauthnUserRegistration).toHaveBeenCalledWith({
headers: {
Authorization: ''
},
body: expectedBody
})
}
)

expect((auth as any).client.startWebauthnAuthentication).toHaveBeenCalledWith({
headers: {
Authorization: ''
test.each([
[
'with all parameters specified',
{
user_id: 'user@example.com',
tenant_id: 'tenant-123',
channel_id: 'custom-channel-id',
client_id: 'custom-client-id'
},
body: {
user_id: 'test-user-id',
channel_id: 'test-channel-id',
client_id: 'test-client-id'
{
user_id: 'user@example.com',
tenant_id: 'tenant-123',
channel_id: 'custom-channel-id',
client_id: 'custom-client-id'
}
})
})
],
[
'defaults optional parameters when empty object is provided',
{},
{
channel_id: config.siteId,
client_id: config.clientId
}
]
])(
'startWebauthnAuthentication %s',
async (
_,
input: Partial<ShopperLoginTypes.startWebauthnAuthenticationBodyType>,
expectedBody: Partial<ShopperLoginTypes.startWebauthnAuthenticationBodyType>
) => {
const auth = new Auth(config)
await auth.startWebauthnAuthentication(
input as ShopperLoginTypes.startWebauthnAuthenticationBodyType
)

test('finishWebauthnAuthentication', async () => {
const auth = new Auth(config)
await auth.finishWebauthnAuthentication({
client_id: 'test-client-id',
channel_id: 'test-channel-id',
credential: PUBLIC_KEY_CREDENTIAL_JSON
})
expect((auth as any).client.startWebauthnAuthentication).toHaveBeenCalledWith({
headers: {
Authorization: ''
},
body: expectedBody
})
}
)

expect((auth as any).client.finishWebauthnAuthentication).toHaveBeenCalledWith({
headers: {
Authorization: ''
test.each([
[
'with all parameters specified',
{
user_id: 'user@example.com',
email: 'user@example.com',
tenant_id: 'tenant-123',
usid: 'usid-123',
channel_id: 'custom-channel-id',
client_id: 'custom-client-id',
credential: PUBLIC_KEY_CREDENTIAL_JSON
},
body: {
client_id: 'test-client-id',
channel_id: 'test-channel-id',
{
user_id: 'user@example.com',
email: 'user@example.com',
tenant_id: 'tenant-123',
usid: 'usid-123',
channel_id: 'custom-channel-id',
client_id: 'custom-client-id',
credential: PUBLIC_KEY_CREDENTIAL_JSON
}
})
})
],
[
'defaults optional parameters when only required parameters are specified',
{
credential: PUBLIC_KEY_CREDENTIAL_JSON
},
{
channel_id: config.siteId,
client_id: config.clientId,
credential: PUBLIC_KEY_CREDENTIAL_JSON
}
]
])(
'finishWebauthnAuthentication %s',
async (
_,
input: Partial<ShopperLoginTypes.AuthenticateFinishRequest>,
expectedBody: Partial<ShopperLoginTypes.AuthenticateFinishRequest>
) => {
const auth = new Auth(config)
await auth.finishWebauthnAuthentication(
input as ShopperLoginTypes.AuthenticateFinishRequest
)

expect((auth as any).client.finishWebauthnAuthentication).toHaveBeenCalledWith({
headers: {
Authorization: ''
},
body: expectedBody
})
}
)
})
8 changes: 4 additions & 4 deletions packages/commerce-sdk-react/src/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1387,9 +1387,7 @@ class Auth {
if (authHeader) {
options.headers.Authorization = authHeader
}
// TODO: no code verifier needed with the fix blair has made, delete this when the fix has been merged to production
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore

const res = await this.client.resetPassword(options)
return res
}
Expand Down Expand Up @@ -1532,12 +1530,14 @@ class Auth {
// Required params
client_id: parameters.client_id || slasClient.clientConfig.parameters.clientId,
channel_id: parameters.channel_id || slasClient.clientConfig.parameters.siteId,
user_id: parameters.user_id,
// Optional params
...(parameters.user_id && {user_id: parameters.user_id}),
...(parameters.tenant_id && {tenant_id: parameters.tenant_id})
}
}

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore TODO: user_id is optional, but commerce-sdk-isomorphic expects it to be required. Remove this comment after commerce-sdk-isomorphic is updated.
Comment on lines +1539 to +1540
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to remove this comment after commerce-sdk-isomorphic is updated with recent OAS change that makes user_id optional

return await slasClient.startWebauthnAuthentication(options)
}

Expand Down
Loading
Loading