This repository has been archived by the owner on May 19, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
/
index.ts
101 lines (81 loc) · 3.22 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import axios from 'axios'
import { decodeJWT } from 'did-jwt'
import { ACCESS_TOKEN_KEY, REFRESH_TOKEN_KEY } from './constants'
import { LocalStorage } from './store'
import { LoginResponse, DIDAuthConfig, PersonalSign, KeyValueStore, DIDAuthStoreConfig, DIDAuthServiceConfig } from './types'
import { Web3Provider } from '../web3provider/types'
class AuthManager {
store: KeyValueStore
did: string
serviceUrl: string
personalSign: PersonalSign
constructor (config: DIDAuthConfig) {
this.store = config.store || new LocalStorage()
this.did = config.did
this.serviceUrl = config.serviceUrl
this.personalSign = config.personalSign
}
// store
private storeTokens = async ({ accessToken, refreshToken }: { accessToken: string, refreshToken: string }) => {
await Promise.all([
this.store.set(ACCESS_TOKEN_KEY, accessToken),
this.store.set(REFRESH_TOKEN_KEY, refreshToken)
])
return { accessToken, refreshToken }
}
private getStoredAccessToken = () => this.store.get(ACCESS_TOKEN_KEY)
private getStoredRefreshToken = () => this.store.get(REFRESH_TOKEN_KEY)
// did auth challenge-response authentication
private getChallenge = (): Promise<string> => axios.get(`${this.serviceUrl}/request-auth/${this.did}`)
.then(res => res.status === 200 && !!res.data && res.data.challenge)
private signChallenge = (challenge: string) => this.personalSign(
`Login to ${this.serviceUrl}\nVerification code: ${challenge}`
).then(sig => ({ did: this.did, sig }))
private login = (): Promise<LoginResponse> => this.getChallenge()
.then(this.signChallenge)
.then(signature => axios.post(`${this.serviceUrl}/auth`, { response: signature }))
.then(res => res.status === 200 && res.data)
.then(this.storeTokens)
private async refreshAccessToken (): Promise<LoginResponse> {
const refreshToken = await this.getStoredRefreshToken()
if (!refreshToken) return this.login()
return axios.post(`${this.serviceUrl}/refresh-token`, { refreshToken })
.then(res => res.status === 200 && res.data)
.then(this.storeTokens)
.catch(err => {
if (err.response.status !== 401) throw err
// if it is expired, do another login
return this.login()
})
}
// api
public async getAccessToken () {
const accessToken = await this.getStoredAccessToken()
if (!accessToken) return this.login().then(tokens => tokens.accessToken)
// TODO: should we verify?
const { payload } = decodeJWT(accessToken)
if (payload.exp <= Math.floor(Date.now() / 1000)) {
return this.refreshAccessToken().then(tokens => tokens.accessToken)
}
return accessToken
}
public storedTokens = () => Promise.all([
this.getStoredAccessToken(),
this.getStoredRefreshToken()
]).then(([accessToken, refreshToken]) => ({
accessToken,
refreshToken
}))
static fromWeb3Provider (config: DIDAuthServiceConfig & DIDAuthStoreConfig, provider: Web3Provider) {
return provider.request({
method: 'eth_accounts'
}).then(accounts => new AuthManager({
...config,
personalSign: (data: string) => provider.request({
method: 'personal_sign',
params: [accounts[0], data]
})
}))
}
}
export default AuthManager