Skip to content

Commit 165c13a

Browse files
committed
Initial session key implementation
1 parent 467f049 commit 165c13a

File tree

16 files changed

+4464
-583
lines changed

16 files changed

+4464
-583
lines changed

.eslintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"root": true,
3-
"ignorePatterns": ["lib/*", "tests/*", "node_modules/**"],
3+
"ignorePatterns": ["lib/*", "tests/*", "node_modules/**", "src/sessionkey/systemcontract.ts"],
44
"extends": [
55
"eslint:recommended",
66
"plugin:@typescript-eslint/eslint-recommended",

.prettierignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
lib/*
2+
tests/*
3+
node_modules/**
4+
src/sessionkey/systemcontract.ts

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@wharfkit/session",
33
"description": "Create account-based sessions, perform transactions, and allow users to login using Antelope-based blockchains.",
4-
"version": "1.6.1",
4+
"version": "2.0.0-rc1",
55
"homepage": "https://github.com/wharfkit/session",
66
"license": "BSD-3-Clause",
77
"main": "lib/session.js",
@@ -20,6 +20,7 @@
2020
"@wharfkit/abicache": "^1.2.1",
2121
"@wharfkit/antelope": "^1.0.11",
2222
"@wharfkit/common": "^1.2.0",
23+
"@wharfkit/contract": "^1.2.1",
2324
"@wharfkit/signing-request": "^3.1.0",
2425
"pako": "^2.0.4",
2526
"tslib": "^2.1.0"
@@ -39,7 +40,6 @@
3940
"@types/node": "^18.7.18",
4041
"@typescript-eslint/eslint-plugin": "^5.20.0",
4142
"@typescript-eslint/parser": "^5.20.0",
42-
"@wharfkit/contract": "^1.0.0",
4343
"@wharfkit/mock-data": "^1.2.1",
4444
"@wharfkit/transact-plugin-resource-provider": "^1.1.0",
4545
"@wharfkit/wallet-plugin-privatekey": "^1.1.0",

src/index-module.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export * from './kit'
22
export * from './login'
33
export * from './session'
4+
export * from './sessionkey'
45
export * from './storage'
56
export * from './transact'
67
export * from './ui'

src/kit.ts

Lines changed: 58 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import type {Contract} from '@wharfkit/contract'
33
import {
44
Checksum256,
55
Checksum256Type,
6-
Name,
76
NameType,
87
PermissionLevel,
98
PermissionLevelType,
@@ -34,6 +33,9 @@ import {
3433
CreateAccountOptions,
3534
CreateAccountResponse,
3635
} from './account-creation'
36+
import {SessionKeyManager} from './sessionkey/manager'
37+
import {SessionKeyWalletPlugin} from './sessionkey/wallet'
38+
import {SessionKeyConfig} from './sessionkey/types'
3739

3840
export interface LoginOptions {
3941
arbitrary?: Record<string, any> // Arbitrary data that will be passed via context to wallet plugin
@@ -56,6 +58,7 @@ export interface LoginResult {
5658
export interface LogoutContext {
5759
session: Session
5860
appName: string
61+
ui?: UserInterface
5962
}
6063

6164
export interface RestoreArgs {
@@ -84,6 +87,7 @@ export interface SessionKitOptions {
8487
transactPlugins?: TransactPlugin[]
8588
transactPluginsOptions?: TransactPluginsOptions
8689
accountCreationPlugins?: AccountCreationPlugin[]
90+
sessionKey?: SessionKeyConfig
8791
}
8892

8993
/**
@@ -102,6 +106,7 @@ export class SessionKit {
102106
readonly ui: UserInterface
103107
readonly walletPlugins: WalletPlugin[]
104108
readonly accountCreationPlugins: AccountCreationPlugin[] = []
109+
readonly sessionKeyManager?: SessionKeyManager
105110
public chains: ChainDefinition[]
106111

107112
constructor(args: SessionKitArgs, options: SessionKitOptions = {}) {
@@ -161,6 +166,17 @@ export class SessionKit {
161166
if (options.accountCreationPlugins) {
162167
this.accountCreationPlugins = options.accountCreationPlugins
163168
}
169+
170+
// Initialize session key support if configured
171+
if (options.sessionKey) {
172+
this.sessionKeyManager = new SessionKeyManager(options.sessionKey, this.ui)
173+
this.walletPlugins = [
174+
...this.walletPlugins,
175+
new SessionKeyWalletPlugin({
176+
walletPlugins: this.walletPlugins,
177+
}),
178+
]
179+
}
164180
}
165181

166182
/**
@@ -187,6 +203,16 @@ export class SessionKit {
187203
return chain
188204
}
189205

206+
/**
207+
* Find a wallet plugin by its ID.
208+
*
209+
* @param id The wallet plugin ID to search for
210+
* @returns The wallet plugin if found, undefined otherwise
211+
*/
212+
getWalletPlugin(id: string): WalletPlugin | undefined {
213+
return this.walletPlugins.find((plugin) => plugin.id === id)
214+
}
215+
190216
/**
191217
* Request account creation.
192218
*/
@@ -350,6 +376,7 @@ export class SessionKit {
350376
retrievePublicKey: plugin.retrievePublicKey?.bind(plugin),
351377
}
352378
}),
379+
sessionKeyManager: this.sessionKeyManager,
353380
})
354381

355382
// Tell the UI a login request is beginning.
@@ -362,10 +389,9 @@ export class SessionKit {
362389
context.walletPluginIndex = 0
363390
context.uiRequirements.requiresWalletSelect = false
364391
} else if (options?.walletPlugin) {
365-
const index = this.walletPlugins.findIndex((p) => p.id === options.walletPlugin)
366-
if (index >= 0) {
367-
walletPlugin = this.walletPlugins[index]
368-
context.walletPluginIndex = index
392+
walletPlugin = this.getWalletPlugin(options.walletPlugin)
393+
if (walletPlugin) {
394+
context.walletPluginIndex = this.walletPlugins.indexOf(walletPlugin)
369395
context.uiRequirements.requiresWalletSelect = false
370396
}
371397
}
@@ -470,6 +496,9 @@ export class SessionKit {
470496
this.getSessionOptions(options)
471497
)
472498

499+
// Make session available to afterLogin hooks
500+
context.session = session
501+
473502
// Call the `afterLogin` hooks that were registered by the LoginPlugins
474503
for (const hook of context.hooks.afterLogin) await hook(context)
475504

@@ -496,6 +525,7 @@ export class SessionKit {
496525
return {
497526
session,
498527
appName: this.appName,
528+
ui: this.ui,
499529
}
500530
} else {
501531
return {
@@ -508,6 +538,7 @@ export class SessionKit {
508538
walletPlugin,
509539
}),
510540
appName: this.appName,
541+
ui: this.ui,
511542
}
512543
}
513544
}
@@ -516,53 +547,40 @@ export class SessionKit {
516547
if (!this.storage) {
517548
throw new Error('An instance of Storage must be provided to utilize the logout method.')
518549
}
519-
await this.storage.remove('session')
520550
if (session) {
521-
const walletPlugin = this.walletPlugins.find(
522-
(wPlugin) => session?.walletPlugin.id === wPlugin.id
523-
)
551+
// Use the session's wallet plugin directly if it's a Session instance
552+
// (it may be a wrapped SessionKeyWalletPlugin with data)
553+
const walletPlugin =
554+
session instanceof Session
555+
? session.walletPlugin
556+
: this.getWalletPlugin(session.walletPlugin.id)
524557

525558
if (walletPlugin?.logout) {
526559
await walletPlugin.logout(this.logoutParams(session, walletPlugin))
527560
}
528561

562+
await this.storage.remove('session')
563+
529564
const sessions = await this.getSessions()
530565
if (sessions) {
531-
let serialized = session
532-
if (session instanceof Session) {
533-
serialized = session.serialize()
534-
}
535-
const other = sessions.filter((s: Record<string, any>) => {
536-
return (
537-
!Checksum256.from(s.chain).equals(
538-
Checksum256.from(String(serialized.chain))
539-
) ||
540-
!Name.from(s.actor).equals(Name.from(serialized.actor)) ||
541-
!Name.from(s.permission).equals(Name.from(serialized.permission))
542-
)
543-
})
566+
const other = sessions.filter((s) => !Session.matches(s, session))
544567
await this.storage.write('sessions', JSON.stringify(other))
545568
}
546569
} else {
547570
const sessions = await this.getSessions()
548571

549-
await this.storage.remove('sessions')
550-
551572
if (sessions) {
552-
Promise.all(
553-
sessions.map((s) => {
554-
const walletPlugin = this.walletPlugins.find(
555-
(wPlugin) => s.walletPlugin.id === wPlugin.id
556-
)
573+
for (const s of sessions) {
574+
const walletPlugin = this.getWalletPlugin(s.walletPlugin.id)
557575

558-
if (walletPlugin?.logout) {
559-
return walletPlugin.logout(this.logoutParams(s, walletPlugin))
560-
} else {
561-
return Promise.resolve()
562-
}
563-
})
564-
)
576+
if (walletPlugin?.logout) {
577+
await walletPlugin.logout(this.logoutParams(s, walletPlugin))
578+
}
579+
}
565580
}
581+
582+
await this.storage.remove('session')
583+
await this.storage.remove('sessions')
566584
}
567585
}
568586

@@ -633,13 +651,7 @@ export class SessionKit {
633651
return
634652
}
635653

636-
// Ensure a WalletPlugin was found with the provided ID.
637-
const walletPlugin = this.walletPlugins.find((p) => {
638-
if (!args) {
639-
return false
640-
}
641-
return p.id === serializedSession.walletPlugin.id
642-
})
654+
const walletPlugin = this.getWalletPlugin(serializedSession.walletPlugin.id)
643655

644656
if (!walletPlugin) {
645657
throw new Error(
@@ -719,15 +731,7 @@ export class SessionKit {
719731
if (existing) {
720732
const stored = JSON.parse(existing)
721733
const sessions: SerializedSession[] = stored
722-
// Filter out any matching session to ensure no duplicates
723-
.filter((s: SerializedSession): boolean => {
724-
return (
725-
!Checksum256.from(s.chain).equals(Checksum256.from(serialized.chain)) ||
726-
!Name.from(s.actor).equals(Name.from(serialized.actor)) ||
727-
!Name.from(s.permission).equals(Name.from(serialized.permission))
728-
)
729-
})
730-
// Remove the default status from all other sessions for this chain
734+
.filter((s: SerializedSession) => !Session.matches(s, serialized))
731735
.map((s: SerializedSession): SerializedSession => {
732736
if (session.chain.id.equals(s.chain)) {
733737
s.default = false
@@ -783,6 +787,8 @@ export class SessionKit {
783787
transactPlugins: options?.transactPlugins || this.transactPlugins,
784788
transactPluginsOptions: options?.transactPluginsOptions || this.transactPluginsOptions,
785789
ui: this.ui,
790+
sessionKeyManager: this.sessionKeyManager,
791+
onPersist: (session: Session) => this.persistSession(session),
786792
}
787793
}
788794
}

src/login.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {ChainDefinition, Fetch} from '@wharfkit/common'
1010
import {SigningRequestEncodingOptions} from '@wharfkit/signing-request'
1111
import zlib from 'pako'
1212

13+
import type {Session} from './session'
1314
import {UserInterface} from './ui'
1415
import {WalletPluginConfig, WalletPluginMetadata} from './wallet'
1516

@@ -39,6 +40,7 @@ export interface LoginContextOptions {
3940
permissionLevel?: PermissionLevel
4041
walletPlugins?: UserInterfaceWalletPlugin[]
4142
ui: UserInterface
43+
sessionKeyManager?: any
4244
}
4345

4446
export interface UserInterfaceRequirements {
@@ -72,6 +74,11 @@ export class LoginContext {
7274
beforeLogin: [],
7375
}
7476
permissionLevel?: PermissionLevel
77+
/**
78+
* The session created during login.
79+
* Only available in afterLogin hooks.
80+
*/
81+
session?: Session
7582
ui: UserInterface
7683
uiRequirements: UserInterfaceRequirements = {
7784
requiresChainSelect: true,
@@ -81,6 +88,7 @@ export class LoginContext {
8188
}
8289
walletPluginIndex?: number
8390
walletPlugins: UserInterfaceWalletPlugin[] = []
91+
sessionKeyManager?: any
8492
constructor(options: LoginContextOptions) {
8593
this.appName = String(options.appName)
8694
if (options.arbitrary) {
@@ -96,6 +104,7 @@ export class LoginContext {
96104
this.fetch = options.fetch
97105
this.permissionLevel = options.permissionLevel
98106
this.walletPlugins = options.walletPlugins || []
107+
this.sessionKeyManager = options.sessionKeyManager
99108
this.ui = options.ui
100109
options.loginPlugins?.forEach((plugin: AbstractLoginPlugin) => {
101110
plugin.register(this)

0 commit comments

Comments
 (0)