Skip to content
Merged
Show file tree
Hide file tree
Changes from 58 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
ef2c877
feat: add account created toast and register passkey modal
hajinsuha1 Nov 5, 2025
38a634a
feat: rename and refactor passkey registration components
hajinsuha1 Nov 5, 2025
82aad01
feat: add hardcoded config for local passkey registration testing
hajinsuha1 Nov 6, 2025
318856f
feat: implement passkey registration with email verification
hajinsuha1 Nov 7, 2025
39cb5c8
feat: centralize passkey registration prompt to account page
hajinsuha1 Nov 7, 2025
ad550f7
refactor: standardize session storage handling for new accounts
hajinsuha1 Nov 7, 2025
51f587a
display create passkey prompt upon showing account page
hajinsuha1 Jan 2, 2026
56a40fb
Merge remote-tracking branch 'origin/W-19604275-webauthn-spike' into …
jeremy-jung1 Jan 7, 2026
6598ee3
Merge remote-tracking branch 'origin/feature/webauthn-login' into W-2…
jeremy-jung1 Jan 15, 2026
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
5d0a567
Merge remote-tracking branch 'origin/feature/webauthn-login' into W-2…
jeremy-jung1 Jan 22, 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
1870877
Just some initial commit
jeremy-jung1 Jan 24, 2026
7203494
Merge remote-tracking branch 'origin/W-20224267-passkey-login-in-chec…
jeremy-jung1 Jan 24, 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
c76aaaa
more changes
jeremy-jung1 Jan 26, 2026
8b62155
Merge remote-tracking branch 'origin/W-20224267-passkey-login-in-chec…
jeremy-jung1 Jan 27, 2026
447bd05
Update index.jsx
jeremy-jung1 Jan 27, 2026
d9b517e
Update useAuthHelper.ts
jeremy-jung1 Jan 27, 2026
d6eb18b
remove registration
jeremy-jung1 Jan 27, 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
a6ef6bf
changes
jeremy-jung1 Jan 28, 2026
3c43ddc
remove translations
jeremy-jung1 Jan 28, 2026
b73f130
Merge remote-tracking branch 'origin/feature/webauthn-login' into W-2…
jeremy-jung1 Jan 28, 2026
ca42fa8
updates
jeremy-jung1 Jan 28, 2026
e36f2db
More updates
jeremy-jung1 Jan 28, 2026
3cfda69
Updates
jeremy-jung1 Jan 28, 2026
e8f952d
tests
jeremy-jung1 Jan 28, 2026
8043eb5
Merge remote-tracking branch 'origin/W-20224267-passkey-login-in-chec…
jeremy-jung1 Jan 28, 2026
ca4f4e4
changes
jeremy-jung1 Jan 28, 2026
4a6adf0
update
jeremy-jung1 Jan 28, 2026
789d93a
Update en-XA.json
jeremy-jung1 Jan 28, 2026
f5d234b
tests
jeremy-jung1 Jan 28, 2026
7cfb97f
Address some feedback
jeremy-jung1 Jan 29, 2026
d915b2c
Update tests
jeremy-jung1 Jan 29, 2026
1776ebb
Merge branch 'feature/webauthn-login' into W-20224220-passkey-in-auth…
jeremy-jung1 Jan 30, 2026
4786611
Merge branch 'feature/webauthn-login' into W-20224220-passkey-in-auth…
jeremy-jung1 Jan 30, 2026
8ba060c
Merge branch 'feature/webauthn-login' into W-20224220-passkey-in-auth…
jeremy-jung1 Jan 30, 2026
8d46da1
Merge branch 'feature/webauthn-login' into W-20224220-passkey-in-auth…
jeremy-jung1 Feb 1, 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: '[email protected]',
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: '[email protected]',
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: '[email protected]',
mode: 'email'
},
{
user_id: '[email protected]',
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: '[email protected]',
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: '[email protected]',
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: '[email protected]',
pwd_action_token: 'test-pwd-action-token'
},
{
user_id: '[email protected]',
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: '[email protected]',
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: '[email protected]',
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: '[email protected]',
credential: PUBLIC_KEY_CREDENTIAL_JSON,
channel_id: 'test-channel-id',
pwd_action_token: 'test-pwd-action-token'
},
{
username: '[email protected]',
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: '[email protected]',
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: '[email protected]',
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: '[email protected]',
email: '[email protected]',
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: '[email protected]',
email: '[email protected]',
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
})
}
)
})
4 changes: 3 additions & 1 deletion packages/commerce-sdk-react/src/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1532,12 +1532,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.
return await slasClient.startWebauthnAuthentication(options)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export default function useLoginFields({
placeholder: '[email protected]',
defaultValue: '',
type: 'email',
autoComplete: 'email',
// Include 'webauthn' for passkey autofill support
autoComplete: 'email webauthn',
rules: {
required: formatMessage({
defaultMessage: 'Please enter your email address.',
Expand Down
Loading
Loading