Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"root": true,
"ignorePatterns": ["lib/*", "tests/*", "node_modules/**"],
"ignorePatterns": ["lib/*", "tests/*", "node_modules/**", "src/sessionkey/systemcontract.ts"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
Expand Down
4 changes: 4 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
lib/*
tests/*
node_modules/**
src/sessionkey/systemcontract.ts
16 changes: 10 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@wharfkit/session",
"description": "Create account-based sessions, perform transactions, and allow users to login using Antelope-based blockchains.",
"version": "1.6.1",
"version": "2.0.0-rc4",
"homepage": "https://github.com/wharfkit/session",
"license": "BSD-3-Clause",
"main": "lib/session.js",
Expand All @@ -16,11 +16,15 @@
"scripts": {
"prepare": "make"
},
"resolutions": {
"@wharfkit/antelope": "^1.1.1"
},
"dependencies": {
"@wharfkit/abicache": "^1.2.1",
"@wharfkit/antelope": "^1.0.11",
"@wharfkit/antelope": "^1.1.1",
"@wharfkit/common": "^1.2.0",
"@wharfkit/signing-request": "^3.1.0",
"@wharfkit/contract": "^1.2.1",
"@wharfkit/signing-request": "^3.4.0",
"pako": "^2.0.4",
"tslib": "^2.1.0"
},
Expand All @@ -39,8 +43,7 @@
"@types/node": "^18.7.18",
"@typescript-eslint/eslint-plugin": "^5.20.0",
"@typescript-eslint/parser": "^5.20.0",
"@wharfkit/contract": "^1.0.0",
"@wharfkit/mock-data": "^1.2.1",
"@wharfkit/mock-data": "^1.3.1",
"@wharfkit/transact-plugin-resource-provider": "^1.1.0",
"@wharfkit/wallet-plugin-privatekey": "^1.1.0",
"chai": "^4.3.4",
Expand All @@ -62,5 +65,6 @@
"typedoc-plugin-mermaid": "^1.10.0",
"typescript": "^4.1.2",
"yarn-deduplicate": "^6.0.2"
}
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
1 change: 1 addition & 0 deletions src/index-module.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './kit'
export * from './login'
export * from './session'
export * from './sessionkey'
export * from './storage'
export * from './transact'
export * from './ui'
Expand Down
122 changes: 66 additions & 56 deletions src/kit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import type {Contract} from '@wharfkit/contract'
import {
Checksum256,
Checksum256Type,
Name,
NameType,
PermissionLevel,
PermissionLevelType,
Expand Down Expand Up @@ -34,6 +33,9 @@ import {
CreateAccountOptions,
CreateAccountResponse,
} from './account-creation'
import {SessionKeyManager} from './sessionkey/manager'
import {SessionKeyWalletPlugin} from './sessionkey/wallet'
import {SessionKeyConfig} from './sessionkey/types'

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

export interface RestoreArgs {
Expand Down Expand Up @@ -84,6 +87,7 @@ export interface SessionKitOptions {
transactPlugins?: TransactPlugin[]
transactPluginsOptions?: TransactPluginsOptions
accountCreationPlugins?: AccountCreationPlugin[]
sessionKey?: SessionKeyConfig
}

/**
Expand All @@ -102,6 +106,7 @@ export class SessionKit {
readonly ui: UserInterface
readonly walletPlugins: WalletPlugin[]
readonly accountCreationPlugins: AccountCreationPlugin[] = []
readonly sessionKeyManager?: SessionKeyManager
public chains: ChainDefinition[]

constructor(args: SessionKitArgs, options: SessionKitOptions = {}) {
Expand Down Expand Up @@ -161,6 +166,17 @@ export class SessionKit {
if (options.accountCreationPlugins) {
this.accountCreationPlugins = options.accountCreationPlugins
}

// Initialize session key support if configured
if (options.sessionKey) {
this.sessionKeyManager = new SessionKeyManager(options.sessionKey, this.ui)
this.walletPlugins = [
...this.walletPlugins,
new SessionKeyWalletPlugin({
walletPlugins: this.walletPlugins,
}),
]
}
}

/**
Expand All @@ -187,6 +203,16 @@ export class SessionKit {
return chain
}

/**
* Find a wallet plugin by its ID.
*
* @param id The wallet plugin ID to search for
* @returns The wallet plugin if found, undefined otherwise
*/
getWalletPlugin(id: string): WalletPlugin | undefined {
return this.walletPlugins.find((plugin) => plugin.id === id)
}

/**
* Request account creation.
*/
Expand Down Expand Up @@ -331,6 +357,10 @@ export class SessionKit {
*/
async login(options?: LoginOptions): Promise<LoginResult> {
try {
const selectableWalletPlugins = this.walletPlugins.filter(
(plugin) => plugin.id !== 'session-key-wallet'
)

// Create LoginContext for this login request.
const context = new LoginContext({
appName: this.appName,
Expand All @@ -343,29 +373,29 @@ export class SessionKit {
fetch: this.fetch,
loginPlugins: this.loginPlugins,
ui: this.ui,
walletPlugins: this.walletPlugins.map((plugin): UserInterfaceWalletPlugin => {
walletPlugins: selectableWalletPlugins.map((plugin): UserInterfaceWalletPlugin => {
return {
config: plugin.config,
metadata: WalletPluginMetadata.from(plugin.metadata),
retrievePublicKey: plugin.retrievePublicKey?.bind(plugin),
}
}),
sessionKeyManager: this.sessionKeyManager,
})

// Tell the UI a login request is beginning.
await context.ui.onLogin()

// Predetermine WalletPlugin (if possible) to prevent uneeded UI interactions.
let walletPlugin: WalletPlugin | undefined = undefined
if (this.walletPlugins.length === 1) {
walletPlugin = this.walletPlugins[0] // Default to first when only one.
context.walletPluginIndex = 0
if (selectableWalletPlugins.length === 1) {
walletPlugin = selectableWalletPlugins[0] // Default to first when only one.
context.walletPluginIndex = this.walletPlugins.indexOf(walletPlugin)
context.uiRequirements.requiresWalletSelect = false
} else if (options?.walletPlugin) {
const index = this.walletPlugins.findIndex((p) => p.id === options.walletPlugin)
if (index >= 0) {
walletPlugin = this.walletPlugins[index]
context.walletPluginIndex = index
walletPlugin = this.getWalletPlugin(options.walletPlugin)
if (walletPlugin) {
context.walletPluginIndex = this.walletPlugins.indexOf(walletPlugin)
context.uiRequirements.requiresWalletSelect = false
}
}
Expand Down Expand Up @@ -470,6 +500,9 @@ export class SessionKit {
this.getSessionOptions(options)
)

// Make session available to afterLogin hooks
context.session = session

// Call the `afterLogin` hooks that were registered by the LoginPlugins
for (const hook of context.hooks.afterLogin) await hook(context)

Expand All @@ -496,6 +529,7 @@ export class SessionKit {
return {
session,
appName: this.appName,
ui: this.ui,
}
} else {
return {
Expand All @@ -508,6 +542,7 @@ export class SessionKit {
walletPlugin,
}),
appName: this.appName,
ui: this.ui,
}
}
}
Expand All @@ -516,53 +551,40 @@ export class SessionKit {
if (!this.storage) {
throw new Error('An instance of Storage must be provided to utilize the logout method.')
}
await this.storage.remove('session')
if (session) {
const walletPlugin = this.walletPlugins.find(
(wPlugin) => session?.walletPlugin.id === wPlugin.id
)
// Use the session's wallet plugin directly if it's a Session instance
// (it may be a wrapped SessionKeyWalletPlugin with data)
const walletPlugin =
session instanceof Session
? session.walletPlugin
: this.getWalletPlugin(session.walletPlugin.id)

if (walletPlugin?.logout) {
await walletPlugin.logout(this.logoutParams(session, walletPlugin))
}

await this.storage.remove('session')

const sessions = await this.getSessions()
if (sessions) {
let serialized = session
if (session instanceof Session) {
serialized = session.serialize()
}
const other = sessions.filter((s: Record<string, any>) => {
return (
!Checksum256.from(s.chain).equals(
Checksum256.from(String(serialized.chain))
) ||
!Name.from(s.actor).equals(Name.from(serialized.actor)) ||
!Name.from(s.permission).equals(Name.from(serialized.permission))
)
})
const other = sessions.filter((s) => !Session.matches(s, session))
await this.storage.write('sessions', JSON.stringify(other))
}
} else {
const sessions = await this.getSessions()

await this.storage.remove('sessions')

if (sessions) {
Promise.all(
sessions.map((s) => {
const walletPlugin = this.walletPlugins.find(
(wPlugin) => s.walletPlugin.id === wPlugin.id
)
for (const s of sessions) {
const walletPlugin = this.getWalletPlugin(s.walletPlugin.id)

if (walletPlugin?.logout) {
return walletPlugin.logout(this.logoutParams(s, walletPlugin))
} else {
return Promise.resolve()
}
})
)
if (walletPlugin?.logout) {
await walletPlugin.logout(this.logoutParams(s, walletPlugin))
}
}
}

await this.storage.remove('session')
await this.storage.remove('sessions')
}
}

Expand Down Expand Up @@ -633,13 +655,7 @@ export class SessionKit {
return
}

// Ensure a WalletPlugin was found with the provided ID.
const walletPlugin = this.walletPlugins.find((p) => {
if (!args) {
return false
}
return p.id === serializedSession.walletPlugin.id
})
const walletPlugin = this.getWalletPlugin(serializedSession.walletPlugin.id)

if (!walletPlugin) {
throw new Error(
Expand Down Expand Up @@ -719,15 +735,7 @@ export class SessionKit {
if (existing) {
const stored = JSON.parse(existing)
const sessions: SerializedSession[] = stored
// Filter out any matching session to ensure no duplicates
.filter((s: SerializedSession): boolean => {
return (
!Checksum256.from(s.chain).equals(Checksum256.from(serialized.chain)) ||
!Name.from(s.actor).equals(Name.from(serialized.actor)) ||
!Name.from(s.permission).equals(Name.from(serialized.permission))
)
})
// Remove the default status from all other sessions for this chain
.filter((s: SerializedSession) => !Session.matches(s, serialized))
.map((s: SerializedSession): SerializedSession => {
if (session.chain.id.equals(s.chain)) {
s.default = false
Expand Down Expand Up @@ -783,6 +791,8 @@ export class SessionKit {
transactPlugins: options?.transactPlugins || this.transactPlugins,
transactPluginsOptions: options?.transactPluginsOptions || this.transactPluginsOptions,
ui: this.ui,
sessionKeyManager: this.sessionKeyManager,
onPersist: (session: Session) => this.persistSession(session),
}
}
}
9 changes: 9 additions & 0 deletions src/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {ChainDefinition, Fetch} from '@wharfkit/common'
import {SigningRequestEncodingOptions} from '@wharfkit/signing-request'
import zlib from 'pako'

import type {Session} from './session'
import {UserInterface} from './ui'
import {WalletPluginConfig, WalletPluginMetadata} from './wallet'

Expand Down Expand Up @@ -39,6 +40,7 @@ export interface LoginContextOptions {
permissionLevel?: PermissionLevel
walletPlugins?: UserInterfaceWalletPlugin[]
ui: UserInterface
sessionKeyManager?: any
}

export interface UserInterfaceRequirements {
Expand Down Expand Up @@ -72,6 +74,11 @@ export class LoginContext {
beforeLogin: [],
}
permissionLevel?: PermissionLevel
/**
* The session created during login.
* Only available in afterLogin hooks.
*/
session?: Session
ui: UserInterface
uiRequirements: UserInterfaceRequirements = {
requiresChainSelect: true,
Expand All @@ -81,6 +88,7 @@ export class LoginContext {
}
walletPluginIndex?: number
walletPlugins: UserInterfaceWalletPlugin[] = []
sessionKeyManager?: any
constructor(options: LoginContextOptions) {
this.appName = String(options.appName)
if (options.arbitrary) {
Expand All @@ -96,6 +104,7 @@ export class LoginContext {
this.fetch = options.fetch
this.permissionLevel = options.permissionLevel
this.walletPlugins = options.walletPlugins || []
this.sessionKeyManager = options.sessionKeyManager
this.ui = options.ui
options.loginPlugins?.forEach((plugin: AbstractLoginPlugin) => {
plugin.register(this)
Expand Down
Loading