Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SDK-2161] Allow to skip the redirect callback #86

Merged
merged 12 commits into from
Dec 2, 2020
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ ng add @auth0/auth0-angular
- [Protect a route](#protect-a-route)
- [Call an API](#call-an-api)
- [Dynamic configuration](#dynamic-configuration)
- [Using multiple OAuth providers](#using-multiple-oauth-providers)

### Register the authentication module

Expand Down Expand Up @@ -311,7 +312,7 @@ export class MyComponent {
}
```

## Dynamic Configuration
### Dynamic Configuration
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this header was wrong, updated it to be the same level of heading as the other titles.


Instead of using `AuthModule.forRoot` to specify auth configuration, you can provide a factory function using `APP_INITIALIZER` to load your config from an external source before the auth module is loaded, and provide your configuration using `AuthClientConfig.set`:

Expand Down Expand Up @@ -349,6 +350,19 @@ providers: [
],
```

### Using multiple OAuth providers

If your application is making use of multiple OAuth providers, you might need to use multiple callback paths as well, one for each OAuth provider.
frederikprijck marked this conversation as resolved.
Show resolved Hide resolved
To ensure the SDK does not process the callback for any provider other than Auth0, you should configure the AuthModule by setting `skipRedirectCallback`.
frederikprijck marked this conversation as resolved.
Show resolved Hide resolved

```js
AuthModule.forRoot({
skipRedirectCallback: window.location.pathname === '/other-callback',
});
```

**Note**: In the above example, `/other-callback` is an existing route that will be called by any other OAuth provider with a `code` (or `error` in case something went wrong) and `state`.

## Angular Universal

This library makes use of the `window` object in a couple of places during initialization, as well as `sessionStorage` in the underlying Auth0 SPA SDK, and thus [will have problems](https://github.com/angular/universal/blob/master/docs/gotchas.md#window-is-not-defined) when being used in an Angular Universal project. The recommendation currently is to only import this library into a module that is to be used in the browser, and omit it from any module that is to participate in a server-side environment.
Expand Down
17 changes: 17 additions & 0 deletions projects/auth0-angular/src/lib/auth.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,23 @@ export interface AuthConfig {
*/
redirectUri?: string;

/**
* By default, if the page url has code/state params, the SDK will treat them as Auth0's and attempt to exchange the
* code for a token. In some cases the code might be for something else (another OAuth SDK perhaps). In these
* instances you can instruct the client to ignore them by setting the skipRedirectCallback.
frederikprijck marked this conversation as resolved.
Show resolved Hide resolved
*
* ```js
* AuthModule.forRoot({
* skipRedirectCallback: window.location.pathname === '/other-callback'
* })
* ```
*
* **Note**: In the above example, `/other-callback` is an existing route that will be called
* by any other OAuth provider with a `code` (or `error` in case when something went wrong) and `state`.
*
*/
skipRedirectCallback?: boolean;

/**
* The value in seconds used to account for clock skew in JWT expirations.
* Typically, this value is no more than a minute or two at maximum.
Expand Down
18 changes: 18 additions & 0 deletions projects/auth0-angular/src/lib/auth.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Auth0Client, IdToken } from '@auth0/auth0-spa-js';
import { AbstractNavigator } from './abstract-navigator';
import { filter } from 'rxjs/operators';
import { Location } from '@angular/common';
import { AuthConfig, AuthConfigService } from './auth.config';

/**
* Wraps service.isLoading$ so that assertions can be made
Expand All @@ -19,12 +20,14 @@ describe('AuthService', () => {
let moduleSetup: any;
let service: AuthService;
let locationSpy: jasmine.SpyObj<Location>;
let authConfig: Partial<AuthConfig>;

const createService = () => {
return TestBed.inject(AuthService);
};

beforeEach(() => {
authConfig = {};
auth0Client = new Auth0Client({
domain: '',
client_id: '',
Expand Down Expand Up @@ -172,6 +175,10 @@ describe('AuthService', () => {
provide: Location,
useValue: locationSpy,
},
{
provide: AuthConfigService,
useValue: authConfig,
},
],
});

Expand All @@ -187,6 +194,17 @@ describe('AuthService', () => {
});
});

it('should not handle the callback when skipRedirectCallback is true', (done) => {
authConfig.skipRedirectCallback = true;

const localService = createService();

loaded(localService).subscribe(() => {
expect(auth0Client.handleRedirectCallback).not.toHaveBeenCalledTimes(1);
frederikprijck marked this conversation as resolved.
Show resolved Hide resolved
done();
});
});

it('should redirect to the correct route', (done) => {
const localService = createService();

Expand Down
12 changes: 8 additions & 4 deletions projects/auth0-angular/src/lib/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
import { Auth0ClientService } from './auth.client';
import { AbstractNavigator } from './abstract-navigator';
import { Location } from '@angular/common';
import { AuthClientConfig } from './auth.config';

@Injectable({
providedIn: 'root',
Expand Down Expand Up @@ -88,6 +89,7 @@ export class AuthService implements OnDestroy {

constructor(
@Inject(Auth0ClientService) private auth0Client: Auth0Client,
private configFactory: AuthClientConfig,
private location: Location,
private navigator: AbstractNavigator
) {
Expand Down Expand Up @@ -253,11 +255,13 @@ export class AuthService implements OnDestroy {

private shouldHandleCallback(): Observable<boolean> {
return of(this.location.path()).pipe(
map(
(search) =>
map((search) => {
return (
(search.includes('code=') || search.includes('error=')) &&
search.includes('state=')
)
search.includes('state=') &&
!this.configFactory.get()?.skipRedirectCallback
);
})
);
}

Expand Down