Skip to content

Commit

Permalink
Fix readme
Browse files Browse the repository at this point in the history
  • Loading branch information
Kadi Kraman committed Sep 7, 2020
1 parent 769543a commit 6ee33b3
Showing 1 changed file with 80 additions and 104 deletions.
184 changes: 80 additions & 104 deletions exchanges/auth/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,124 +18,104 @@ You'll then need to add the `authExchange`, that this package exposes to your `u

```js
import { createClient, dedupExchange, cacheExchange, Operation } from 'urql';
import { executeExchange } from '@urql/exchange-execute';

/**
* your auth state can be anything
* this is a generic example with a token and refreshToken, but it can
* include token expiry for example if you want to check that in willAuthError
* without having to decode the token every time
**/

type AuthState = {
token: String;
refreshToken: String;
} | null;
import { authExchange } from '@urql/exchange-auth';

const client = createClient({
url: 'http://localhost:1234/graphql',
exchanges: [
dedupExchange,
cacheExchange,
authExchange({
addAuthToOperation: ({
authState,
operation,
}: {
authState: AuthState;
operation: Operation;
}): Operation => {
// the token isn't in the auth state, return the operation without changes
if (!authState || !authState.token) {
return operation;
}

const { token } = authState;

const authHeader = {
Authorization: token,
};

const fetchOptions =
typeof operation.context.fetchOptions === 'function'
? operation.context.fetchOptions()
: operation.context.fetchOptions || {};

return {
...operation,
context: {
...operation.context,
fetchOptions: {
...fetchOptions,
headers: {
...fetchOptions.headers,
...authHeader,
},
addAuthToOperation: ({
authState,
operation,
}) => {
// the token isn't in the auth state, return the operation without changes
if (!authState || !authState.token) {
return operation;
}

// fetchOptions can be a function (See Client API) but you can simplify this based on usage
const fetchOptions =
typeof operation.context.fetchOptions === 'function'
? operation.context.fetchOptions()
: operation.context.fetchOptions || {};

return {
...operation,
context: {
...operation.context,
fetchOptions: {
...fetchOptions,
headers: {
...fetchOptions.headers,
"Authorization": authState.token,
},
},
};
},
willAuthError: ({ authState }) => {
if (!authState) return true;
// e.g. check for expiration, existence of auth etc
return false;
},
didAuthError: ({ error }) => {
// check if the error was an auth error (this can be implemented in various ways, e.g. 401 or a special error code)
return error.graphQLErrors.some(
e => e.extensions?.code === 'FORBIDDEN',
);
},
getAuth: async ({ authState, mutate }) => {
// for initial launch, fetch the auth state from storage (local storage, async storage etc)
if (!authState) {
const token = localStorage.getItem('token');
const refreshToken = localStorage.getItem('refreshToken');
if (token && refreshToken) {
return { token, refreshToken };
}
return null;
}

/**
* the following code gets exetuted when an auth error has occurred
* we should refresh the token if possible and return a new auth state
* If refrsh fails, we should log out
**/

// if your refresh logic is in graphQL, you must use this mutate function to call it
// if your refresh logic is a separate RESTful endpoint, use fetch or similar
const result = await mutate(refreshMutation, {
token: authState!.refreshToken,
});

if (result.data?.refreshLogin) {
// save the new tokens in storage for next restart
localStorage.setItem('token', result.data.refreshLogin.token);
localStorage.setItem('refreshToken', result.data.refreshLogin.refreshToken);

// return the new tokens
return {
token: result.data.refreshLogin.token,
refreshToken: result.data.refreshLogin.refreshToken,
};
},
};
},
willAuthError: ({ authState }) => {
if (!authState) return true;
// e.g. check for expiration, existence of auth etc
return false;
},
didAuthError: ({ error }) => {
// check if the error was an auth error (this can be implemented in various ways, e.g. 401 or a special error code)
return error.graphQLErrors.some(
e => e.extensions?.code === 'FORBIDDEN',
);
},
getAuth: async ({ authState, mutate }) => {
// for initial launch, fetch the auth state from storage (local storage, async storage etc)
if (!authState) {
const token = localStorage.getItem('token');
const refreshToken = localStorage.getItem('refreshToken');
if (token && refreshToken) {
return { token, refreshToken };
}
return null;
}

/**
* the following code gets exetuted when an auth error has occurred
* we should refresh the token if possible and return a new auth state
* If refrsh fails, we should log out
**/

// if your refresh logic is in graphQL, you must use this mutate function to call it
// if your refresh logic is a separate RESTful endpoint, use fetch or similar
const result = await mutate(refreshMutation, {
token: authState!.refreshToken,
});

if (result.data?.refreshLogin) {
// save the new tokens in storage for next restart
localStorage.setItem('token', result.data.refreshLogin.token);
localStorage.setItem('refreshToken', result.data.refreshLogin.refreshToken);

// return the new tokens
return {
token: result.data.refreshLogin.token,
refreshToken: result.data.refreshLogin.refreshToken,
};
}

// otherwise, if refresh fails, log clear storage and log out
localStorage.clear();
// otherwise, if refresh fails, log clear storage and log out
localStorage.clear();

// your app logout logic should trigger here
logout();
// your app logout logic should trigger here
logout();

return null;
},
}),
return null;
},
}),
fetchExchange
],
});
```
## Handling rrrors via the errorExchange
## Handling Errors via the errorExchange
Handling the logout logic in `getAuth` is the easiest way to get started, but it means the errors will always get swallowed by the `authExchange`.
If you want to handle errors globally, this can be done using the `errorExchange`:
Expand All @@ -157,7 +137,3 @@ errorExchange({
}
}),
```
## Maintenance Status
**Active:** Formidable is actively working on this project, and we expect to continue for work for the foreseeable future. Bug reports, feature requests and pull requests are welcome.

0 comments on commit 6ee33b3

Please sign in to comment.