Skip to content

Commit 6a44e06

Browse files
Authorize implicit session (#738)
* Authorize implicit session * Move code * Delete signature request after use * Implicit tests * Fmt * Raw leaf does not require signature * Avoid using as * Separate prepare and complete for implicit session authorization
2 parents bd271ad + b5b56fc commit 6a44e06

File tree

13 files changed

+385
-84
lines changed

13 files changed

+385
-84
lines changed

packages/wallet/primitives/src/attestation.ts

+30-2
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,40 @@ export function encodeAuthData(authData: AuthData): Bytes.Bytes {
3636
)
3737
}
3838

39+
export function decode(bytes: Bytes.Bytes): Attestation {
40+
const approvedSigner = Bytes.toHex(bytes.slice(0, 20))
41+
const identityType = bytes.slice(20, 24)
42+
const issuerHash = bytes.slice(24, 56)
43+
const audienceHash = bytes.slice(56, 88)
44+
const applicationDataLength = Bytes.toNumber(bytes.slice(88, 91))
45+
const applicationData = bytes.slice(91, 91 + applicationDataLength)
46+
const authData = decodeAuthData(bytes.slice(91 + applicationDataLength))
47+
48+
return {
49+
approvedSigner,
50+
identityType,
51+
issuerHash,
52+
audienceHash,
53+
applicationData,
54+
authData,
55+
}
56+
}
57+
58+
export function decodeAuthData(bytes: Bytes.Bytes): AuthData {
59+
const redirectUrlLength = Bytes.toNumber(bytes.slice(0, 3))
60+
const redirectUrl = Bytes.toString(bytes.slice(3, 3 + redirectUrlLength))
61+
62+
return {
63+
redirectUrl,
64+
}
65+
}
66+
3967
export function hash(attestation: Attestation): Bytes.Bytes {
4068
return Hash.keccak256(encode(attestation))
4169
}
4270

43-
export function toJson(attestation: Attestation): string {
44-
return JSON.stringify(encodeForJson(attestation))
71+
export function toJson(attestation: Attestation, indent?: number): string {
72+
return JSON.stringify(encodeForJson(attestation), null, indent)
4573
}
4674

4775
export function encodeForJson(attestation: Attestation): any {

packages/wallet/primitives/src/payload.ts

+29-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { AbiFunction, AbiParameters, Address, Bytes, Hash, Hex, TypedData } from 'ox'
1+
import { AbiFunction, AbiParameters, Address, Bytes, Hash, Hex } from 'ox'
2+
import { getSignPayload } from 'ox/TypedData'
23
import { RECOVER_SAPIENT_SIGNATURE } from './constants.js'
4+
import { Attestation } from './index.js'
35
import { minBytesFor } from './utils.js'
4-
import { getSignPayload } from 'ox/TypedData'
56

67
export type Call = {
78
to: Address.Address
@@ -35,11 +36,17 @@ export type Digest = {
3536
digest: Hex.Hex
3637
}
3738

39+
export type SessionImplicitAuthorize = {
40+
type: 'session-implicit-authorize'
41+
sessionAddress: Address.Address
42+
attestation: Attestation.Attestation
43+
}
44+
3845
export type Parent = {
3946
parentWallets?: Address.Address[]
4047
}
4148

42-
export type Payload = Calls | Message | ConfigUpdate | Digest
49+
export type Payload = Calls | Message | ConfigUpdate | Digest | SessionImplicitAuthorize
4350

4451
export type Parented = Payload & Parent
4552

@@ -97,6 +104,14 @@ export function isConfigUpdate(payload: Payload): payload is ConfigUpdate {
97104
return payload.type === 'config-update'
98105
}
99106

107+
export function isDigest(payload: Payload): payload is Digest {
108+
return payload.type === 'digest'
109+
}
110+
111+
export function isSessionImplicitAuthorize(payload: Payload): payload is SessionImplicitAuthorize {
112+
return payload.type === 'session-implicit-authorize'
113+
}
114+
100115
export function encode(payload: Calls, self?: Address.Address): Bytes.Bytes {
101116
const callsLen = payload.calls.length
102117
const nonceBytesNeeded = minBytesFor(payload.nonce)
@@ -295,6 +310,12 @@ export function encodeSapient(
295310
}
296311

297312
export function hash(wallet: Address.Address, chainId: bigint, payload: Parented): Bytes.Bytes {
313+
if (isDigest(payload)) {
314+
return Bytes.fromHex(payload.digest)
315+
}
316+
if (isSessionImplicitAuthorize(payload)) {
317+
return Attestation.hash(payload.attestation)
318+
}
298319
const typedData = toTyped(wallet, chainId, payload)
299320
return Bytes.fromHex(getSignPayload(typedData))
300321
}
@@ -395,7 +416,11 @@ export function toTyped(wallet: Address.Address, chainId: bigint, payload: Paren
395416
}
396417

397418
case 'digest': {
398-
throw new Error('Digest is not supported - Use message instead')
419+
throw new Error('Digest does not support typed data - Use message instead')
420+
}
421+
422+
case 'session-implicit-authorize': {
423+
throw new Error('Payload does not support typed data')
399424
}
400425
}
401426
}

packages/wallet/primitives/src/signature.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ export function isRawTopology(cand: any): cand is RawTopology {
172172
}
173173

174174
export function isRawLeaf(cand: any): cand is RawLeaf {
175-
return typeof cand === 'object' && 'weight' in cand && 'signature' in cand && !('tree' in cand)
175+
return typeof cand === 'object' && 'weight' in cand && !('tree' in cand)
176176
}
177177

178178
export function isRawNestedLeaf(cand: any): cand is RawNestedLeaf {
@@ -1389,5 +1389,8 @@ function encode(
13891389
digest: payload.digest,
13901390
parentWallets: payload.parentWallets ?? [],
13911391
}
1392+
1393+
default:
1394+
throw new Error('Invalid payload type')
13921395
}
13931396
}

packages/wallet/wdk/src/identity/signer.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export class IdentitySigner implements Signers.Signer {
1414
if (!Address.validate(this.authKey.identitySigner)) {
1515
throw new Error('No signer address found')
1616
}
17-
return this.authKey.identitySigner as `0x${string}`
17+
return this.authKey.identitySigner
1818
}
1919

2020
async sign(

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ export class AuthCodePkceHandler extends IdentityHandler implements Handler {
1212

1313
constructor(
1414
public readonly signupKind: 'google-pkce' | 'apple-pkce',
15-
private readonly issuer: string,
16-
private readonly audience: string,
15+
public readonly issuer: string,
16+
public readonly audience: string,
1717
nitro: Identity.IdentityInstrument,
1818
signatures: Signatures,
1919
private readonly commitments: Db.AuthCommitments,
2020
authKeys: Db.AuthKeys,
2121
) {
22-
super(nitro, authKeys, signatures)
22+
super(nitro, authKeys, signatures, Identity.IdentityType.OIDC)
2323
}
2424

2525
public get kind() {

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

+16
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,27 @@ import * as Identity from '../../identity/index.js'
44
import { Signatures } from '../signatures.js'
55
import { BaseSignatureRequest } from '../types/signature-request.js'
66

7+
export const identityTypeToHex = (identityType?: Identity.IdentityType): Hex.Hex => {
8+
// Bytes4
9+
switch (identityType) {
10+
case Identity.IdentityType.Guest:
11+
return '0x00000000'
12+
case Identity.IdentityType.Email:
13+
return '0x00000001'
14+
case Identity.IdentityType.OIDC:
15+
return '0x00000002'
16+
default:
17+
// Unknown identity type
18+
return '0xffffffff'
19+
}
20+
}
21+
722
export class IdentityHandler {
823
constructor(
924
private readonly nitro: Identity.IdentityInstrument,
1025
private readonly authKeys: Db.AuthKeys,
1126
private readonly signatures: Signatures,
27+
public readonly identityType: Identity.IdentityType,
1228
) {}
1329

1430
public onStatusChange(cb: () => void): () => void {

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export class OtpHandler extends IdentityHandler implements Handler {
1616
private onPromptOtp: undefined | ((recipient: string, respond: RespondFn) => Promise<void>)
1717

1818
constructor(nitro: Identity.IdentityInstrument, signatures: Signatures, authKeys: Db.AuthKeys) {
19-
super(nitro, authKeys, signatures)
19+
super(nitro, authKeys, signatures, Identity.IdentityType.Email)
2020
}
2121

2222
public registerUI(onPromptOtp: (recipient: string, respond: RespondFn) => Promise<void>) {
@@ -36,7 +36,7 @@ export class OtpHandler extends IdentityHandler implements Handler {
3636
throw new Error('otp-handler-ui-not-registered')
3737
}
3838

39-
const challenge = Identity.OtpChallenge.fromRecipient(Identity.IdentityType.Email, email)
39+
const challenge = Identity.OtpChallenge.fromRecipient(this.identityType, email)
4040
const { loginHint, challenge: codeChallenge } = await this.nitroCommitVerifier(challenge)
4141

4242
return new Promise(async (resolve, reject) => {
@@ -87,7 +87,7 @@ export class OtpHandler extends IdentityHandler implements Handler {
8787
message: 'request-otp',
8888
handle: () =>
8989
new Promise(async (resolve, reject) => {
90-
const challenge = Identity.OtpChallenge.fromSigner(Identity.IdentityType.Email, address)
90+
const challenge = Identity.OtpChallenge.fromSigner(this.identityType, address)
9191
const { loginHint, challenge: codeChallenge } = await this.nitroCommitVerifier(challenge)
9292

9393
const respond = async (otp: string) => {

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

+35-14
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,38 @@
11
import { Signers as CoreSigners, Relayer, State } from '@0xsequence/wallet-core'
2-
import { Config, Constants, Context, Extensions, Network, Payload, SessionConfig } from '@0xsequence/wallet-primitives'
2+
import {
3+
Attestation,
4+
Config,
5+
Constants,
6+
Context,
7+
Extensions,
8+
Network,
9+
Payload,
10+
SessionConfig,
11+
Signature as SequenceSignature,
12+
} from '@0xsequence/wallet-primitives'
313
import { Address } from 'ox'
414
import * as Db from '../dbs/index.js'
515
import * as Identity from '../identity/index.js'
616
import { Devices } from './devices.js'
717
import {
8-
Handler,
9-
DevicesHandler,
10-
PasskeysHandler,
1118
AuthCodePkceHandler,
19+
DevicesHandler,
20+
Handler,
1221
MnemonicHandler,
1322
OtpHandler,
23+
PasskeysHandler,
1424
} from './handlers/index.js'
25+
import { Janitor } from './janitor.js'
1526
import { Logger } from './logger.js'
16-
import { Sessions } from './sessions.js'
27+
import { AuthorizeImplicitSessionArgs, Sessions } from './sessions.js'
1728
import { Signatures } from './signatures.js'
1829
import { Signers } from './signers.js'
1930
import { Transactions } from './transactions.js'
2031
import { BaseSignatureRequest, SignatureRequest, Wallet } from './types/index.js'
21-
import { Transaction, TransactionRequest } from './types/transaction-request.js'
22-
import { CompleteRedirectArgs, LoginArgs, SignupArgs, StartSignUpWithRedirectArgs, Wallets } from './wallets.js'
2332
import { Kinds } from './types/signer.js'
33+
import { Transaction, TransactionRequest } from './types/transaction-request.js'
2434
import { WalletSelectionUiHandler } from './types/wallet.js'
25-
import { Janitor } from './janitor.js'
35+
import { CompleteRedirectArgs, LoginArgs, SignupArgs, StartSignUpWithRedirectArgs, Wallets } from './wallets.js'
2636

2737
export type ManagerOptions = {
2838
verbose?: boolean
@@ -329,6 +339,10 @@ export class Manager {
329339
return this.shared.modules.wallets.unregisterWalletSelector(handler)
330340
}
331341

342+
public async getConfiguration(wallet: Address.Address) {
343+
return this.shared.modules.wallets.getConfiguration({ wallet })
344+
}
345+
332346
// Signatures
333347

334348
public async listSignatureRequests(): Promise<SignatureRequest[]> {
@@ -416,8 +430,19 @@ export class Manager {
416430
return this.shared.modules.sessions.getSessionTopology(walletAddress)
417431
}
418432

419-
public async addImplicitSession(walletAddress: Address.Address, sessionAddress: Address.Address) {
420-
return this.shared.modules.sessions.addImplicitSession(walletAddress, sessionAddress)
433+
public async prepareAuthorizeImplicitSession(
434+
walletAddress: Address.Address,
435+
sessionAddress: Address.Address,
436+
args: AuthorizeImplicitSessionArgs,
437+
): Promise<string> {
438+
return this.shared.modules.sessions.prepareAuthorizeImplicitSession(walletAddress, sessionAddress, args)
439+
}
440+
441+
public async completeAuthorizeImplicitSession(requestId: string): Promise<{
442+
attestation: Attestation.Attestation
443+
signature: SequenceSignature.RSY
444+
}> {
445+
return this.shared.modules.sessions.completeAuthorizeImplicitSession(requestId)
421446
}
422447

423448
public async addExplicitSession(
@@ -448,8 +473,4 @@ export class Manager {
448473
console.log('Completing session update:', requestId)
449474
return this.shared.modules.sessions.completeSessionUpdate(sigRequest.wallet, requestId)
450475
}
451-
452-
public async getConfiguration(wallet: Address.Address) {
453-
return this.shared.modules.wallets.getConfiguration({ wallet })
454-
}
455476
}

0 commit comments

Comments
 (0)