@sanity/sdk-react 3.3.0 → 3.4.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +139 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +144 -26
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/_exports/sdk-react.ts +3 -0
- package/src/components/auth/AuthBoundary.test.tsx +81 -2
- package/src/components/auth/AuthBoundary.tsx +17 -3
- package/src/components/auth/LoginCallback.test.tsx +46 -7
- package/src/components/auth/LoginCallback.tsx +22 -4
- package/src/hooks/auth/useHandleOAuthCallback.test.tsx +16 -0
- package/src/hooks/auth/useHandleOAuthCallback.tsx +49 -0
- package/src/hooks/auth/useOAuthAuthorize.test.tsx +16 -0
- package/src/hooks/auth/useOAuthAuthorize.tsx +28 -0
- package/src/hooks/auth/useOAuthTokens.test.tsx +240 -0
- package/src/hooks/auth/useOAuthTokens.tsx +95 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getOAuthTokensState,
|
|
3
|
+
type OAuthTokens,
|
|
4
|
+
refreshOAuthTokens,
|
|
5
|
+
revokeOAuthTokens,
|
|
6
|
+
} from '@sanity/sdk'
|
|
7
|
+
|
|
8
|
+
import {createCallbackHook} from '../helpers/createCallbackHook'
|
|
9
|
+
import {createStateSourceHook} from '../helpers/createStateSourceHook'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The current OAuth token state, plus actions to refresh and revoke it.
|
|
13
|
+
*
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
export interface UseOAuthTokensResult {
|
|
17
|
+
/** The stored OAuth tokens, or `null` when not logged in via OAuth */
|
|
18
|
+
tokens: OAuthTokens | null
|
|
19
|
+
/**
|
|
20
|
+
* Returns whether the access token has expired, comparing `expiresAt` against
|
|
21
|
+
* the current time at the moment it is called. Reading the clock does not
|
|
22
|
+
* trigger a re-render, so call this in an event handler or effect rather than
|
|
23
|
+
* during render. Returns `false` when there are no tokens.
|
|
24
|
+
*/
|
|
25
|
+
isExpired: () => boolean
|
|
26
|
+
/**
|
|
27
|
+
* Refresh via the OAuth `refresh_token` grant. When there is no refresh token,
|
|
28
|
+
* core clears the stored tokens, logs the user out, and this resolves `null`.
|
|
29
|
+
* Rejects on transient failures (network, 5xx, 408, 429), leaving tokens
|
|
30
|
+
* unchanged so the call can be retried. Also rejects when the server rejects
|
|
31
|
+
* the refresh token itself (other 4xx); core clears the tokens and logs out
|
|
32
|
+
* first, so check `tokens` before retrying.
|
|
33
|
+
*/
|
|
34
|
+
refresh: () => Promise<OAuthTokens | null>
|
|
35
|
+
/** Revoke the tokens at the OAuth server, clear them locally, and log out. */
|
|
36
|
+
revoke: () => Promise<void>
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const useOAuthTokensState = createStateSourceHook(getOAuthTokensState)
|
|
40
|
+
const useRefreshOAuthTokens = createCallbackHook(refreshOAuthTokens)
|
|
41
|
+
const useRevokeOAuthTokens = createCallbackHook(revokeOAuthTokens)
|
|
42
|
+
|
|
43
|
+
function isOAuthTokenExpired(tokens: OAuthTokens | null): boolean {
|
|
44
|
+
return tokens ? tokens.expiresAt.getTime() <= Date.now() : false
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* A React hook that exposes the stored OAuth token state along with `refresh`
|
|
49
|
+
* and `revoke` actions.
|
|
50
|
+
*
|
|
51
|
+
* @remarks
|
|
52
|
+
* The token view is a synchronous read over core's token state source, so the
|
|
53
|
+
* hook re-renders whenever tokens change — including changes made in another
|
|
54
|
+
* tab, which core propagates via `storage` events.
|
|
55
|
+
*
|
|
56
|
+
* @returns The current {@link UseOAuthTokensResult}
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```tsx
|
|
60
|
+
* function TokenStatus() {
|
|
61
|
+
* const {tokens, isExpired, refresh, revoke} = useOAuthTokens()
|
|
62
|
+
*
|
|
63
|
+
* if (!tokens) return <div>Not signed in</div>
|
|
64
|
+
*
|
|
65
|
+
* const handleRefresh = async () => {
|
|
66
|
+
* if (!isExpired()) return
|
|
67
|
+
* try {
|
|
68
|
+
* await refresh()
|
|
69
|
+
* } catch {
|
|
70
|
+
* // Transient failure (tokens unchanged, retry later) or the refresh
|
|
71
|
+
* // token was rejected (tokens now null, user is logged out).
|
|
72
|
+
* }
|
|
73
|
+
* }
|
|
74
|
+
*
|
|
75
|
+
* return (
|
|
76
|
+
* <div>
|
|
77
|
+
* <p>Expires at {tokens.expiresAt.toLocaleTimeString()}</p>
|
|
78
|
+
* <button onClick={handleRefresh}>Refresh if expired</button>
|
|
79
|
+
* <button onClick={() => revoke()}>Revoke tokens</button>
|
|
80
|
+
* </div>
|
|
81
|
+
* )
|
|
82
|
+
* }
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* @public
|
|
86
|
+
*/
|
|
87
|
+
export function useOAuthTokens(): UseOAuthTokensResult {
|
|
88
|
+
const tokens = useOAuthTokensState()
|
|
89
|
+
return {
|
|
90
|
+
tokens,
|
|
91
|
+
isExpired: () => isOAuthTokenExpired(tokens),
|
|
92
|
+
refresh: useRefreshOAuthTokens(),
|
|
93
|
+
revoke: useRevokeOAuthTokens(),
|
|
94
|
+
}
|
|
95
|
+
}
|