Skip to content
This repository has been archived by the owner on Dec 3, 2021. It is now read-only.

Commit

Permalink
feat: Enhancement/custom token fetch fn (#29)
Browse files Browse the repository at this point in the history
* fix access_token passthrough

* changed fetch function

* Custom fetch functions

fix: #28, #30
  • Loading branch information
aguscha333 authored and adambrgmn committed Aug 29, 2018
1 parent ddbed5d commit 4e398fb
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 2 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@ redirected from the OAuth2-provider.
| `location` | `{ search: string }` | no | - | Used to extract info from querystring [(read more below)](#location-and-querystring) |
| `querystring` | `string` | no | - | Used to extract info from querystring [(read more below)](#location-and-querystring) |
| `tokenFetchArgs` | `object` | no | `{}` | Used to fetch the token endpoint [(read more below)](#tokenfetchargs) |
| `tokenFn` | `func` | no | `null` | Used to bypass default fetch function to fetch the token [(read more below)](#tokenfn) |


#### Events

Expand Down Expand Up @@ -274,6 +276,20 @@ merged and overriden with the `tokenFetchArgs`:
{ method: 'GET', headers: { 'Content-Type': 'application/json' }}
```

#### `tokenFn`

The prop `tokenFn` can be used to change how the token is fetched and received from
the service. It's a way to bypass the default fetch all together and use your own.
For example, if your `access-token` comes in the headers instead of the response body
you will have to use your own fetch function to get those. Or perhaps you already
have a custom built fetch function that communicates with your backend and you want
to make use of it.

Your function will receive the `url` from the OauthReceiver, it takes the
`tokenUrl` and builds it up with all the other needed parameters so you don't have to.
It will also receive the `tokenFetchArgs` parameter just in case you need it. if you don't,
just ignore it.

### `createOauthFlow`

```js
Expand Down
81 changes: 81 additions & 0 deletions src/OauthReceiver/OauthReceiver.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,87 @@ describe('Component <OauthReceiver />', () => {

await waitForElement(() => getByTestId('done'));

expect(fetch2).toHaveBeenCalledWith(
expect.stringContaining(props.tokenUrl),
expect.objectContaining({
method: expect.stringMatching('POST'),
cache: expect.stringMatching('no-cache'),
headers: expect.objectContaining({
'Content-Type': expect.stringMatching(
'application/x-www-form-urlencoded',
),
}),
}),
);
});
test('with custom fetch function, default args', async () => {
const onAuthSuccess = jest.fn();
const onAuthError = jest.fn();

const props = {
tokenUrl: 'https://api.service.com/oauth2/token',
tokenFn: fetch2,
clientId: 'abc',
clientSecret: 'abcdef',
redirectUri: 'https://www.test.com/redirect',
querystring: `?${qs.stringify({
code: 'abc',
state: JSON.stringify({ from: '/success' }),
})}`,
onAuthSuccess,
onAuthError,
};

const { getByTestId } = render(
<OauthReceiver
{...props}
render={({ processing, state }) => (
<div>
{processing && <span data-testid="done">done</span>}
<span data-testid="state">{state && state.from}</span>
</div>
)}
/>,
);

await waitForElement(() => getByTestId('done'));

expect(onAuthSuccess).toHaveBeenCalledTimes(1);
expect(onAuthError).not.toHaveBeenCalled();

expect(getByTestId('state')).toHaveTextContent('/success');
});

test('with custom token function and token uri fetch args', async () => {
fetch2.mockClear();

const props = {
tokenUrl: 'https://api.service.com/oauth2/token',
tokenFn: fetch2,
tokenFetchArgs: {
cache: 'no-cache',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
},
clientId: 'abc',
clientSecret: 'abcdef',
redirectUri: 'https://www.test.com/redirect',
querystring: `?${qs.stringify({
code: 'abc',
state: JSON.stringify({ from: '/settings' }),
})}`,
};

const { getByTestId } = render(
<OauthReceiver
{...props}
render={({ processing }) => (
<div>{processing && <span data-testid="done">done</span>}</div>
)}
/>,
);

await waitForElement(() => getByTestId('done'));

expect(fetch2).toHaveBeenCalledWith(
expect.stringContaining(props.tokenUrl),
expect.objectContaining({
Expand Down
9 changes: 7 additions & 2 deletions src/OauthReceiver/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class OauthReceiver extends React.Component {
clientSecret,
redirectUri,
args,
tokenFn,
onAuthSuccess,
} = this.props;

Expand Down Expand Up @@ -60,8 +61,10 @@ class OauthReceiver extends React.Component {
const defaultFetchArgs = { method: 'POST', headers };
const fetchArgs = Object.assign(defaultFetchArgs, tokenFetchArgs);

fetch2(url, fetchArgs)
.then(response => {
(typeof tokenFn === 'function' ?
tokenFn(url, fetchArgs) :
fetch2(url, fetchArgs)
).then(response => {
const accessToken = response.access_token;

if (typeof onAuthSuccess === 'function') {
Expand Down Expand Up @@ -140,6 +143,7 @@ OauthReceiver.propTypes = {
),
location: PropTypes.shape({ search: PropTypes.string.isRequired }),
querystring: PropTypes.string,
tokenFn: PropTypes.func,
onAuthSuccess: PropTypes.func,
onAuthError: PropTypes.func,
render: PropTypes.func,
Expand All @@ -154,6 +158,7 @@ OauthReceiver.defaultProps = {
args: {},
location: null,
querystring: null,
tokenFn: null,
onAuthSuccess: null,
onAuthError: null,
render: null,
Expand Down

0 comments on commit 4e398fb

Please sign in to comment.