Resource Authentication
All network requests are run through the static getFetchInit
optionally
defined in your Resource
.
Cookie Auth
Here's an example using simple cookie auth:
- fetch
- superagent
import { Resource } from '@rest-hooks/rest';
abstract class AuthdResource extends Resource {
static getFetchInit = (init: RequestInit): RequestInit => ({
...init,
credentials: 'same-origin',
});
}
import { Resource } from '@rest-hooks/rest';
import type { SuperAgentRequest } from 'superagent';
abstract class AuthdResource extends Resource {
static fetchPlugin = (request: SuperAgentRequest) =>
request.withCredentials();
}
If you used the custom superagent fetch
You can also do more complex flows (like adding arbitrary headers) to the request. Every getFetchInit() takes in the existing init options of fetch, and returns new init options to be used.
Access Tokens
- static member
- function singleton
import { getAuthToken } from 'authorization-singleton';
import { Resource } from '@rest-hooks/rest';
abstract class AuthdResource extends Resource {
declare static accessToken?: string;
static getFetchInit = (init: RequestInit): RequestInit => ({
...init,
headers: {
...init.headers,
'Access-Token': this.accessToken,
},
});
}
Upon login we set the token:
import AuthdResource from 'resources/AuthdResource';
function Auth() {
const handleLogin = useCallback(
async e => {
const { accessToken } = await login(new FormData(e.target));
// success!
AuthdResource.accessToken = accessToken;
},
[login],
);
return <AuthForm onSubmit={handleLogin} />;
}
import { getAuthToken } from 'authorization-singleton';
import { Resource } from '@rest-hooks/rest';
abstract class AuthdResource extends Resource {
static getFetchInit = (init: RequestInit): RequestInit => ({
...init,
headers: {
...init.headers,
'Access-Token': getAuthToken(),
},
});
}
Upon login we set the token:
import { setAuthToken } from 'authorization-singleton';
import AuthdResource from 'resources/AuthdResource';
function Auth() {
const handleLogin = useCallback(
async e => {
const { accessToken } = await login(new FormData(e.target));
// success!
setAuthToken(accessToken);
},
[login],
);
return <AuthForm onSubmit={handleLogin} />;
}
Auth Headers from React Context
Using React Context for state that is not displayed (like auth tokens) is not recommended.
Here we use a context variable to set headers. Note - this means any endpoint functions can only be called from a React Component. (However, this should be fine since the context will only exist in React anyway.)
HookableResource gives us endpoint methods that are hooks so we can access React context.
import { HookableResource } from '@rest-hooks/rest';
abstract class AuthdResource extends HookableResource {
static useFetchInit = (init: RequestInit) => {
const accessToken = useAuthContext();
return {
...init,
headers: {
...init.headers,
'Access-Token': accessToken,
},
};
};
}
Using this means all endpoint calls must only occur during a function render.
function CreatePost() {
const { fetch } = useController();
// PostResource.useCreate() calls useFetchInit()
const createPost = PostResource.useCreate();
return (
<form
onSubmit={e => fetch(createPost, {}, new FormData(e.target))}
>
{/* ... */}
</form>
);
}
Code organization
If much of your Resources
share a similar auth mechanism, you might
try extending from a base class that defines such common customizations.