Skip to content

Commit

Permalink
dev(auth): refresh token periodically
Browse files Browse the repository at this point in the history
  • Loading branch information
theborakompanioni committed Sep 22, 2023
1 parent 1b11609 commit 5d55606
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 20 deletions.
12 changes: 10 additions & 2 deletions src/components/Wallets.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,12 @@ describe('<Wallets />', () => {
})
apiMock.postWalletUnlock.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve({ walletname: dummyWalletName, token: dummyToken }),
json: () =>
Promise.resolve({
walletname: dummyWalletName,
token: dummyToken,
refresh_token: dummyToken,
}),
})

await act(async () => setup({}))
Expand All @@ -223,7 +228,10 @@ describe('<Wallets />', () => {
await waitFor(() => screen.findByText('wallets.wallet_preview.button_unlock'))
})

expect(mockStartWallet).toHaveBeenCalledWith(dummyWalletName, dummyToken)
expect(mockStartWallet).toHaveBeenCalledWith(dummyWalletName, {
token: dummyToken,
refresh_token: dummyToken,
})
expect(mockedNavigate).toHaveBeenCalledWith('/wallet')
})

Expand Down
2 changes: 2 additions & 0 deletions src/constants/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ export const JM_MINIMUM_MAKERS_DEFAULT = 4
export const CJ_STATE_TAKER_RUNNING = 0
export const CJ_STATE_MAKER_RUNNING = 1
export const CJ_STATE_NONE_RUNNING = 2

export const JM_API_AUTH_TOKEN_EXPIRY: Milliseconds = Math.round(0.5 * 60 * 60 * 1_000)
4 changes: 2 additions & 2 deletions src/context/ServiceInfoContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import { CJ_STATE_TAKER_RUNNING, CJ_STATE_MAKER_RUNNING } from '../constants/con

import * as Api from '../libs/JmWalletApi'

// interval in milliseconds for periodic session requests
const SESSION_REQUEST_INTERVAL = 10_000
// interval for periodic session requests
const SESSION_REQUEST_INTERVAL: Milliseconds = 10_000

type AmountFraction = number
type AmountCounterparties = number
Expand Down
41 changes: 40 additions & 1 deletion src/context/WalletContext.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { createContext, useEffect, useCallback, useState, useContext, PropsWithChildren, useMemo } from 'react'

import { getSession } from '../session'
import { getSession, setSession } from '../session'
import * as fb from '../components/fb/utils'
import * as Api from '../libs/JmWalletApi'

import { WalletBalanceSummary, toBalanceSummary } from './BalanceSummary'
import { JM_API_AUTH_TOKEN_EXPIRY } from '../constants/config'

export interface CurrentWallet {
name: Api.WalletName
Expand Down Expand Up @@ -276,6 +277,44 @@ const WalletProvider = ({ children }: PropsWithChildren<any>) => {
}
}, [currentWallet])

useEffect(() => {
if (!currentWallet) return

const abortCtrl = new AbortController()

const refreshToken = () => {
const session = getSession()
if (!session?.auth?.refresh_token) return

Api.postToken(
{ token: session.auth.token, signal: abortCtrl.signal },
{
grant_type: 'refresh_token',
refresh_token: session.auth.refresh_token,
},
)
.then((res) => (res.ok ? res.json() : Api.Helper.throwError(res)))
.then((body) => {
const auth = {
token: body.token,
token_type: body.token_type,
expires_in: body.expires_in,
scope: body.scope,
refresh_token: body.refresh_token,
}
setSession({ name: currentWallet.name, auth })
setCurrentWallet({ ...currentWallet, token: auth.token })
})
.catch((err) => console.error(err))
}

const interval = setInterval(refreshToken, JM_API_AUTH_TOKEN_EXPIRY / 4)
return () => {
clearInterval(interval)
abortCtrl.abort()
}
}, [currentWallet, setCurrentWallet])

return (
<WalletContext.Provider
value={{
Expand Down
44 changes: 29 additions & 15 deletions src/libs/JmWalletApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,33 @@
*/
const basePath = () => `${window.JM.PUBLIC_PATH}/api`

export type ApiToken = string
export type WalletName = `${string}.jmdat`
type ApiToken = string
type WalletName = `${string}.jmdat`

export type Mixdepth = number
export type AmountSats = number // TODO: should be BigInt! Remove once every caller migrated to TypeScript.
export type BitcoinAddress = string
type Mixdepth = number
type AmountSats = number // TODO: should be BigInt! Remove once every caller migrated to TypeScript.
type BitcoinAddress = string

type Vout = number
export type TxId = string
export type UtxoId = `${TxId}:${Vout}`
type TxId = string
type UtxoId = `${TxId}:${Vout}`

// for JM versions <0.9.11
export type SingleTokenAuthContext = {
type SingleTokenAuthContext = {
token: ApiToken
refresh_token: undefined
}

// for JM versions >=0.9.11
export type RefreshTokenAuthContext = SingleTokenAuthContext & {
type RefreshTokenAuthContext = {
token: ApiToken
token_type: string // "bearer"
expires_in: Seconds // 1800
scope: string
refresh_token: ApiToken
}

export type ApiAuthContext = SingleTokenAuthContext | RefreshTokenAuthContext
type ApiAuthContext = SingleTokenAuthContext | RefreshTokenAuthContext

type WithWalletName = {
walletName: WalletName
Expand All @@ -48,7 +50,7 @@ type WithMixdepth = {
type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
type YYYY = `2${Digit}${Digit}${Digit}`
type MM = '01' | '02' | '03' | '04' | '05' | '06' | '07' | '08' | '09' | '10' | '11' | '12'
export type Lockdate = `${YYYY}-${MM}`
type Lockdate = `${YYYY}-${MM}`
type WithLockdate = {
lockdate: Lockdate
}
Expand All @@ -64,7 +66,7 @@ interface AuthApiRequestContext extends ApiRequestContext {
token: ApiToken
}

export type WalletRequestContext = AuthApiRequestContext & WithWalletName
type WalletRequestContext = AuthApiRequestContext & WithWalletName

interface ApiError {
message: string
Expand Down Expand Up @@ -135,12 +137,12 @@ interface ConfigGetRequest {
field: string
}

export interface StartSchedulerRequest {
interface StartSchedulerRequest {
destination_addresses: BitcoinAddress[]
tumbler_options?: TumblerOptions
}

export interface TumblerOptions {
interface TumblerOptions {
restart?: boolean
schedulefile?: string
addrcount?: number
Expand Down Expand Up @@ -490,7 +492,7 @@ const getRescanBlockchain = async ({
})
}

export class JmApiError extends Error {
class JmApiError extends Error {
public response: Response

constructor(response: Response, message: string) {
Expand Down Expand Up @@ -526,4 +528,16 @@ export {
getSchedule,
getRescanBlockchain,
Helper,
JmApiError,
ApiAuthContext,
StartSchedulerRequest,
WalletRequestContext,
ApiToken,
WalletName,
Lockdate,
TxId,
UtxoId,
Mixdepth,
AmountSats,
BitcoinAddress,
}

0 comments on commit 5d55606

Please sign in to comment.