Skip to content

Commit fab000a

Browse files
committed
wdk: address review comments; refactor auth commitments
1 parent e81b800 commit fab000a

5 files changed

Lines changed: 117 additions & 81 deletions

File tree

packages/wallet/wdk/src/dbs/auth-commitments.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,19 @@ import { IDBPDatabase, IDBPTransaction } from 'idb'
33

44
const TABLE_NAME = 'auth-commitments'
55

6+
export type CommitAuthArgs =
7+
| { type: 'auth'; state?: string }
8+
| { type: 'reauth'; state: string; signer: string }
9+
| { type: 'add-signer'; wallet: string; state?: string }
10+
611
export type AuthCommitment = {
712
id: string
813
kind: 'google-pkce' | 'apple' | `custom-${string}`
914
metadata: { [key: string]: string }
1015
verifier?: string
1116
challenge?: string
1217
target: string
13-
isSignUp: boolean
14-
signer?: string
15-
wallet?: string
16-
}
18+
} & ({ type: 'auth' } | { type: 'reauth'; signer: string } | { type: 'add-signer'; wallet: string })
1719

1820
export class AuthCommitments extends Generic<AuthCommitment, 'id'> {
1921
constructor(dbName: string = 'sequence-auth-commitments') {

packages/wallet/wdk/src/dbs/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export type { AuthCommitment } from './auth-commitments.js'
1+
export type { AuthCommitment, CommitAuthArgs } from './auth-commitments.js'
22
export { AuthCommitments } from './auth-commitments.js'
33

44
export type { AuthKey } from './auth-keys.js'

packages/wallet/wdk/src/sequence/handlers/authcode-pkce.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as Identity from '@0xsequence/identity-instrument'
66
import { IdentitySigner } from '../../identity/signer.js'
77
import { AuthCodeHandler } from './authcode.js'
88
import type { WdkEnv } from '../../env.js'
9+
import type { CommitAuthArgs } from '../../dbs/auth-commitments.js'
910

1011
export class AuthCodePkceHandler extends AuthCodeHandler implements Handler {
1112
constructor(
@@ -22,26 +23,30 @@ export class AuthCodePkceHandler extends AuthCodeHandler implements Handler {
2223
super(signupKind, issuer, oauthUrl, audience, nitro, signatures, commitments, authKeys, env)
2324
}
2425

25-
public async commitAuth(target: string, isSignUp: boolean, state?: string, signer?: string, wallet?: string) {
26+
public async commitAuth(target: string, args: CommitAuthArgs) {
2627
let challenge = new Identity.AuthCodePkceChallenge(this.issuer, this.audience, this.redirectUri)
27-
if (signer) {
28-
challenge = challenge.withSigner({ address: signer, keyType: Identity.KeyType.Ethereum_Secp256k1 })
28+
if (args.type === 'reauth') {
29+
challenge = challenge.withSigner({ address: args.signer, keyType: Identity.KeyType.Ethereum_Secp256k1 })
2930
}
3031
const { verifier, loginHint, challenge: codeChallenge } = await this.nitroCommitVerifier(challenge)
31-
if (!state) {
32-
state = Hex.fromBytes(Bytes.random(32))
33-
}
32+
const state = args.state ?? Hex.fromBytes(Bytes.random(32))
3433

35-
await this.commitments.set({
34+
const base = {
3635
id: state,
37-
kind: this.signupKind,
36+
kind: this.signupKind as Db.AuthCommitment['kind'],
3837
verifier,
3938
challenge: codeChallenge,
4039
target,
4140
metadata: {},
42-
isSignUp,
43-
wallet,
44-
})
41+
}
42+
43+
if (args.type === 'reauth') {
44+
await this.commitments.set({ ...base, type: 'reauth', signer: args.signer })
45+
} else if (args.type === 'add-signer') {
46+
await this.commitments.set({ ...base, type: 'add-signer', wallet: args.wallet })
47+
} else {
48+
await this.commitments.set({ ...base, type: 'auth' })
49+
}
4550

4651
const searchParams = this.serializeQuery({
4752
code_challenge: codeChallenge,

packages/wallet/wdk/src/sequence/handlers/authcode.ts

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { IdentitySigner } from '../../identity/signer.js'
88
import { IdentityHandler } from './identity.js'
99
import { Kinds } from '../types/signer.js'
1010
import type { NavigationLike, WdkEnv } from '../../env.js'
11+
import type { CommitAuthArgs } from '../../dbs/auth-commitments.js'
1112

1213
export class AuthCodeHandler extends IdentityHandler implements Handler {
1314
protected redirectUri: string = ''
@@ -39,20 +40,23 @@ export class AuthCodeHandler extends IdentityHandler implements Handler {
3940
this.redirectUri = redirectUri
4041
}
4142

42-
public async commitAuth(target: string, isSignUp: boolean, state?: string, signer?: string, wallet?: string) {
43-
if (!state) {
44-
state = Hex.fromBytes(Bytes.random(32))
45-
}
43+
public async commitAuth(target: string, args: CommitAuthArgs) {
44+
const state = args.state ?? Hex.fromBytes(Bytes.random(32))
4645

47-
await this.commitments.set({
46+
const base = {
4847
id: state,
49-
kind: this.signupKind,
50-
signer,
48+
kind: this.signupKind as Db.AuthCommitment['kind'],
5149
target,
5250
metadata: {},
53-
isSignUp,
54-
wallet,
55-
})
51+
}
52+
53+
if (args.type === 'reauth') {
54+
await this.commitments.set({ ...base, type: 'reauth', signer: args.signer })
55+
} else if (args.type === 'add-signer') {
56+
await this.commitments.set({ ...base, type: 'add-signer', wallet: args.wallet })
57+
} else {
58+
await this.commitments.set({ ...base, type: 'auth' })
59+
}
5660

5761
const searchParams = this.serializeQuery({
5862
client_id: this.audience,
@@ -70,7 +74,7 @@ export class AuthCodeHandler extends IdentityHandler implements Handler {
7074
code: string,
7175
): Promise<[IdentitySigner, { [key: string]: string }]> {
7276
let challenge = new Identity.AuthCodeChallenge(this.issuer, this.audience, this.redirectUri, code)
73-
if (commitment.signer) {
77+
if (commitment.type === 'reauth') {
7478
challenge = challenge.withSigner({ address: commitment.signer, keyType: Identity.KeyType.Ethereum_Secp256k1 })
7579
}
7680
await this.nitroCommitVerifier(challenge)
@@ -104,7 +108,11 @@ export class AuthCodeHandler extends IdentityHandler implements Handler {
104108
message: 'request-redirect',
105109
handle: async () => {
106110
const navigation = this.getNavigation()
107-
const url = await this.commitAuth(navigation.getPathname(), false, request.id, address)
111+
const url = await this.commitAuth(navigation.getPathname(), {
112+
type: 'reauth',
113+
state: request.id,
114+
signer: address,
115+
})
108116
navigation.redirect(url)
109117
return true
110118
},

packages/wallet/wdk/src/sequence/wallets.ts

Lines changed: 74 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -339,9 +339,15 @@ export interface WalletsInterface {
339339
* via a redirect-based OAuth flow. It validates the wallet, generates the necessary challenges and state,
340340
* stores them locally, and returns a URL. Your application should redirect the user to this URL.
341341
*
342+
* After the redirect callback, call `completeRedirect` with the returned state and code. This will
343+
* create a pending `AddLoginSigner` signature request internally. The caller can then discover
344+
* the pending request through the signatures module and call `completeAddLoginSigner` with the requestId
345+
* once it has been signed.
346+
*
342347
* @param args Arguments specifying the wallet, provider (`kind`), and the `target` URL for the redirect callback.
343348
* @returns A promise that resolves to the full OAuth URL to which the user should be redirected.
344349
* @see {completeRedirect} for the second step of this flow.
350+
* @see {completeAddLoginSigner} to finalize after signing.
345351
*/
346352
startAddLoginSignerWithRedirect(args: StartAddLoginSignerWithRedirectArgs): Promise<string>
347353

@@ -490,6 +496,20 @@ export function isIdTokenArgs(args: SignupArgs): args is IdTokenSignupArgs {
490496
return 'idToken' in args
491497
}
492498

499+
function addLoginSignerToSignupArgs(args: AddLoginSignerArgs): SignupArgs {
500+
switch (args.kind) {
501+
case 'mnemonic':
502+
return { kind: 'mnemonic', mnemonic: args.mnemonic }
503+
case 'email-otp':
504+
return { kind: 'email-otp', email: args.email }
505+
default: {
506+
// google-id-token, apple-id-token, custom-*
507+
const _args = args as { kind: string; idToken: string }
508+
return { kind: _args.kind as IdTokenSignupArgs['kind'], idToken: _args.idToken }
509+
}
510+
}
511+
}
512+
493513
function buildCappedTree(members: { address: Address.Address; imageHash?: Hex.Hex }[]): Config.Topology {
494514
const loginMemberWeight = 1n
495515

@@ -880,7 +900,7 @@ export class Wallets implements WalletsInterface {
880900
if (!(handler instanceof AuthCodeHandler)) {
881901
throw new Error('handler-does-not-support-redirect')
882902
}
883-
return handler.commitAuth(args.target, true)
903+
return handler.commitAuth(args.target, { type: 'auth' })
884904
}
885905

886906
async startAddLoginSignerWithRedirect(args: StartAddLoginSignerWithRedirectArgs) {
@@ -900,7 +920,7 @@ export class Wallets implements WalletsInterface {
900920
if (!(handler instanceof AuthCodeHandler)) {
901921
throw new Error('handler-does-not-support-redirect')
902922
}
903-
return handler.commitAuth(args.target, true, undefined, undefined, args.wallet)
923+
return handler.commitAuth(args.target, { type: 'add-signer', wallet: args.wallet })
904924
}
905925

906926
async completeRedirect(args: CompleteRedirectArgs): Promise<string> {
@@ -909,62 +929,62 @@ export class Wallets implements WalletsInterface {
909929
throw new Error('invalid-state')
910930
}
911931

912-
// If commitment has a wallet, this is an add-login-signer redirect
913-
if (commitment.wallet) {
914-
const handlerKind = getSignupHandlerKey(commitment.kind)
915-
const handler = this.shared.handlers.get(handlerKind)
916-
if (!handler) {
917-
throw new Error('handler-not-registered')
918-
}
919-
if (!(handler instanceof AuthCodeHandler)) {
920-
throw new Error('handler-does-not-support-redirect')
921-
}
922-
923-
const walletAddress = commitment.wallet as Address.Address
924-
const walletEntry = await this.get(walletAddress)
925-
if (!walletEntry) {
926-
throw new Error('wallet-not-found')
927-
}
928-
if (walletEntry.status !== 'ready') {
929-
throw new Error('wallet-not-ready')
930-
}
932+
switch (commitment.type) {
933+
case 'add-signer': {
934+
const handlerKind = getSignupHandlerKey(commitment.kind)
935+
const handler = this.shared.handlers.get(handlerKind)
936+
if (!handler) {
937+
throw new Error('handler-not-registered')
938+
}
939+
if (!(handler instanceof AuthCodeHandler)) {
940+
throw new Error('handler-does-not-support-redirect')
941+
}
931942

932-
const [signer] = await handler.completeAuth(commitment, args.code)
933-
const signerKind = getSignerKindForSignup(commitment.kind)
943+
const walletAddress = commitment.wallet as Address.Address
944+
const walletEntry = await this.get(walletAddress)
945+
if (!walletEntry) {
946+
throw new Error('wallet-not-found')
947+
}
948+
if (walletEntry.status !== 'ready') {
949+
throw new Error('wallet-not-ready')
950+
}
934951

935-
await this.addLoginSignerFromPrepared(walletAddress, {
936-
signer,
937-
extra: { signerKind },
938-
})
952+
const [signer] = await handler.completeAuth(commitment, args.code)
953+
const signerKind = getSignerKindForSignup(commitment.kind)
939954

940-
if (!commitment.target) {
941-
throw new Error('invalid-state')
942-
}
943-
return commitment.target
944-
}
945-
946-
// commitment.isSignUp and signUp also mean 'signIn' from wallet's perspective
947-
if (commitment.isSignUp) {
948-
await this.signUp({
949-
kind: commitment.kind,
950-
commitment,
951-
code: args.code,
952-
noGuard: args.noGuard,
953-
target: commitment.target,
954-
isRedirect: true,
955-
use4337: args.use4337,
956-
})
957-
} else {
958-
const handlerKind = getSignupHandlerKey(commitment.kind)
959-
const handler = this.shared.handlers.get(handlerKind)
960-
if (!handler) {
961-
throw new Error('handler-not-registered')
955+
await this.addLoginSignerFromPrepared(walletAddress, {
956+
signer,
957+
extra: { signerKind },
958+
})
959+
break
962960
}
963-
if (!(handler instanceof AuthCodeHandler)) {
964-
throw new Error('handler-does-not-support-redirect')
961+
962+
case 'auth': {
963+
await this.signUp({
964+
kind: commitment.kind,
965+
commitment,
966+
code: args.code,
967+
noGuard: args.noGuard,
968+
target: commitment.target,
969+
isRedirect: true,
970+
use4337: args.use4337,
971+
})
972+
break
965973
}
966974

967-
await handler.completeAuth(commitment, args.code)
975+
case 'reauth': {
976+
const handlerKind = getSignupHandlerKey(commitment.kind)
977+
const handler = this.shared.handlers.get(handlerKind)
978+
if (!handler) {
979+
throw new Error('handler-not-registered')
980+
}
981+
if (!(handler instanceof AuthCodeHandler)) {
982+
throw new Error('handler-does-not-support-redirect')
983+
}
984+
985+
await handler.completeAuth(commitment, args.code)
986+
break
987+
}
968988
}
969989

970990
if (!commitment.target) {
@@ -1409,7 +1429,8 @@ export class Wallets implements WalletsInterface {
14091429
throw new Error('wallet-not-ready')
14101430
}
14111431

1412-
const loginSigner = await this.prepareSignUp(args as unknown as SignupArgs)
1432+
const signupArgs = addLoginSignerToSignupArgs(args)
1433+
const loginSigner = await this.prepareSignUp(signupArgs)
14131434
return this.addLoginSignerFromPrepared(args.wallet, loginSigner)
14141435
}
14151436

0 commit comments

Comments
 (0)