]>
Commit | Line | Data |
---|---|---|
1 | import { HttpStatusCode, OAuth2ErrorCode, UserRefreshToken } from '../../../../../shared/models' | |
2 | import { OAuthUserTokens, objectToUrlEncoded } from '../../../root-helpers' | |
3 | import { peertubeLocalStorage } from '../../../root-helpers/peertube-web-storage' | |
4 | ||
5 | export class AuthHTTP { | |
6 | private readonly LOCAL_STORAGE_OAUTH_CLIENT_KEYS = { | |
7 | CLIENT_ID: 'client_id', | |
8 | CLIENT_SECRET: 'client_secret' | |
9 | } | |
10 | ||
11 | private userOAuthTokens: OAuthUserTokens | |
12 | ||
13 | private headers = new Headers() | |
14 | ||
15 | constructor () { | |
16 | this.userOAuthTokens = OAuthUserTokens.getUserTokens(peertubeLocalStorage) | |
17 | ||
18 | if (this.userOAuthTokens) this.setHeadersFromTokens() | |
19 | } | |
20 | ||
21 | fetch (url: string, { optionalAuth, method }: { optionalAuth: boolean, method?: string }) { | |
22 | const refreshFetchOptions = optionalAuth | |
23 | ? { headers: this.headers } | |
24 | : {} | |
25 | ||
26 | return this.refreshFetch(url.toString(), { ...refreshFetchOptions, method }) | |
27 | } | |
28 | ||
29 | getHeaderTokenValue () { | |
30 | if (!this.userOAuthTokens) return null | |
31 | ||
32 | return `${this.userOAuthTokens.tokenType} ${this.userOAuthTokens.accessToken}` | |
33 | } | |
34 | ||
35 | isLoggedIn () { | |
36 | return !!this.userOAuthTokens | |
37 | } | |
38 | ||
39 | private refreshFetch (url: string, options?: RequestInit) { | |
40 | return fetch(url, options) | |
41 | .then((res: Response) => { | |
42 | if (res.status !== HttpStatusCode.UNAUTHORIZED_401) return res | |
43 | ||
44 | const refreshingTokenPromise = new Promise<void>((resolve, reject) => { | |
45 | const clientId: string = peertubeLocalStorage.getItem(this.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_ID) | |
46 | const clientSecret: string = peertubeLocalStorage.getItem(this.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_SECRET) | |
47 | ||
48 | const headers = new Headers() | |
49 | headers.set('Content-Type', 'application/x-www-form-urlencoded') | |
50 | ||
51 | const data = { | |
52 | refresh_token: this.userOAuthTokens.refreshToken, | |
53 | client_id: clientId, | |
54 | client_secret: clientSecret, | |
55 | response_type: 'code', | |
56 | grant_type: 'refresh_token' | |
57 | } | |
58 | ||
59 | fetch('/api/v1/users/token', { | |
60 | headers, | |
61 | method: 'POST', | |
62 | body: objectToUrlEncoded(data) | |
63 | }).then(res => { | |
64 | if (res.status === HttpStatusCode.UNAUTHORIZED_401) return undefined | |
65 | ||
66 | return res.json() | |
67 | }).then((obj: UserRefreshToken & { code?: OAuth2ErrorCode }) => { | |
68 | if (!obj || obj.code === OAuth2ErrorCode.INVALID_GRANT) { | |
69 | OAuthUserTokens.flushLocalStorage(peertubeLocalStorage) | |
70 | this.removeTokensFromHeaders() | |
71 | ||
72 | return resolve() | |
73 | } | |
74 | ||
75 | this.userOAuthTokens.accessToken = obj.access_token | |
76 | this.userOAuthTokens.refreshToken = obj.refresh_token | |
77 | OAuthUserTokens.saveToLocalStorage(peertubeLocalStorage, this.userOAuthTokens) | |
78 | ||
79 | this.setHeadersFromTokens() | |
80 | ||
81 | resolve() | |
82 | }).catch((refreshTokenError: any) => { | |
83 | reject(refreshTokenError) | |
84 | }) | |
85 | }) | |
86 | ||
87 | return refreshingTokenPromise | |
88 | .catch(() => { | |
89 | OAuthUserTokens.flushLocalStorage(peertubeLocalStorage) | |
90 | ||
91 | this.removeTokensFromHeaders() | |
92 | }).then(() => fetch(url, { | |
93 | ...options, | |
94 | ||
95 | headers: this.headers | |
96 | })) | |
97 | }) | |
98 | } | |
99 | ||
100 | private setHeadersFromTokens () { | |
101 | this.headers.set('Authorization', this.getHeaderTokenValue()) | |
102 | } | |
103 | ||
104 | private removeTokensFromHeaders () { | |
105 | this.headers.delete('Authorization') | |
106 | } | |
107 | } |