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

Configure when to load AuthWellKnown and possibility to pass it directly #724

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
58d2580
first shot of loading authwellknown eager and lazy
FabianGosebrink May 11, 2020
f153b7a
removed breaking changes
FabianGosebrink May 11, 2020
d29c73c
added more unit tests
FabianGosebrink May 12, 2020
d6da84a
Merge remote-tracking branch 'origin/master' into fabiangosebrink/mov…
FabianGosebrink May 12, 2020
271e103
fixed tests
FabianGosebrink May 12, 2020
c371017
removed configuration property
FabianGosebrink May 12, 2020
db95e1c
adding docs
FabianGosebrink May 12, 2020
fde39a2
fixed the tests
FabianGosebrink May 12, 2020
82fe4a1
added unit tests
FabianGosebrink May 12, 2020
f65d704
improved docs
FabianGosebrink May 12, 2020
b4a7d59
small improvements on null handling
FabianGosebrink May 12, 2020
aae9d75
fixed clearance of authwellknown in storage
FabianGosebrink May 12, 2020
009309d
added logging error
FabianGosebrink May 12, 2020
bf2583a
added config to the docs
FabianGosebrink May 12, 2020
9639b9d
Merge branch 'master' into fabiangosebrink/moving-authwellknown-call-…
damienbod May 12, 2020
acaa660
Adding a link to the features
damienbod May 12, 2020
6d93539
fixing tests
FabianGosebrink May 12, 2020
fd8b1da
Merge branch 'fabiangosebrink/moving-authwellknown-call-into-login-me…
FabianGosebrink May 12, 2020
fc4ec3d
updated samples
FabianGosebrink May 12, 2020
e0b7187
updated samples
FabianGosebrink May 12, 2020
ddc9e6b
small doc improvements
damienbod May 12, 2020
cf9b538
small version changes for next release
damienbod May 12, 2020
75e6b5a
fix typo
FabianGosebrink May 13, 2020
42de988
added correct routes to samples
FabianGosebrink May 13, 2020
9efb502
updated samples
FabianGosebrink May 13, 2020
8dafac3
cleanups
FabianGosebrink May 13, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## Angular Lib for OpenID Connect/OAuth2 Changelog

### 2020-05-13 Version 11.1.0

- Eager loading of well known endpoints can be configured: Made it possible to load the well known endpoints late (per configuration)

### 2020-05-12 Version 11.0.2

- Add configuration property to disable auth_time validation in refresh flows with Azure B2C (Azure B2C implements this incorrectly)
Expand Down
1 change: 1 addition & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ In this document are all the values which can be set to configure this library.
| `storage` | `any` | You can set the storage to `localStorage`, or implement a custom storage (see README). | No |
| `customParams` | `{ [key: string]: string, number, boolean }` | extra parameters can be added to the authorization URL request. | No |
| `disableRefreshIdTokenAuthTimeValidation` | `boolean` | disables the auth_time validation for id_tokens in a refresh due to Azure incorrect implementation | No |
| `eagerLoadAuthWellKnownEndpoints` | `boolean` | Tells if the AuthWellKnownEndpoints should be loaded on start or when the user calls the `authorize` method | No |
18 changes: 18 additions & 0 deletions docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- [Custom parameters](#custom-parameters)
- [OnAuthorizationResult Event](#onauthorizationresult-event)
- [Using the OIDC package in a module or a Angular lib](#using-the-oidc-package-in-a-module-or-a-angular-lib)
- [Delay the loading or pass an existing AuthWellKnownEndpoints config](#delay-the-loading-or-pass-an-existing-authwellknownendpoints-config)

## Public Events

Expand Down Expand Up @@ -199,3 +200,20 @@ export class ChildModule {}
```

The components code is the same then as using it in the main or any other module.

## Delay the loading or pass an existing `.well-known/openid-configuration` configuration

The secure token server `.well-known/openid-configuration` configuration can be requested via an HTTPS call when starting the application in the `APP_INITIALIZER`. This HTTPS call may affect your first page loading time. You can disable this and configure the loading of the `.well-known/openid-configuration` later, just before you start the authentication process. You as a user, can decide when you want to request the well known endpoints.

The property `eagerLoadAuthWellKnownEndpoints` in the configuration sets exactly this. The default is set to `false`, so the `.well-known/openid-configuration` is loaded at the start as in previous versions. Setting this to `true` the `.well-known/openid-configuration` will be loaded when the user starts the authentication.

You also have the option to pass the already existing `.well-known/openid-configuration` into the `withConfig` method as a second parameter. In this case no HTTPS call to load the `.well-known/openid-configuration` will be made.

```typescript
oidcConfigService.withonfig(
{
/* config */
},
{ issuer: 'myIssuer' /* more .well-known/openid-configuration Properties */ }
);
```
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"bugs": {
"url": "https://github.com/damienbod/angular-auth-oidc-client/issues"
},
"version": "11.0.2",
"version": "11.1.0",
"scripts": {
"ng": "ng",
"build": "npm run build-lib",
Expand Down
2 changes: 1 addition & 1 deletion projects/angular-auth-oidc-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@
"authorization"
],
"license": "MIT",
"version": "11.0.2",
"version": "11.1.0",
"description": "Angular Lib for OpenID Connect & OAuth2"
}
9 changes: 8 additions & 1 deletion projects/angular-auth-oidc-client/src/lib/auth.module.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { DataService } from './api/data.service';
import { HttpBaseService } from './api/http-base.service';
import { AuthStateService } from './authState/auth-state.service';
import { AuthWellKnownDataService } from './config/auth-well-known-data.service';
import { AuthWellKnownService } from './config/auth-well-known.service';
import { ConfigurationProvider } from './config/config.provider';
import { OidcConfigService } from './config/config.service';
import { FlowsDataService } from './flows/flows-data.service';
Expand All @@ -13,6 +16,7 @@ import { CheckSessionService } from './iframe/check-session.service';
import { IFrameService } from './iframe/existing-iframe.service';
import { SilentRenewService } from './iframe/silent-renew.service';
import { LoggerService } from './logging/logger.service';
import { LoginService } from './login/login.service';
import { LogoffRevocationService } from './logoffRevoke/logoff-revocation.service';
import { OidcSecurityService } from './oidc.security.service';
import { PublicEventsService } from './public-events/public-events.service';
Expand All @@ -30,7 +34,7 @@ import { StateValidationService } from './validation/state-validation.service';
import { TokenValidationService } from './validation/token-validation.service';

@NgModule({
imports: [CommonModule],
imports: [CommonModule, HttpClientModule],
declarations: [],
exports: [],
})
Expand Down Expand Up @@ -62,6 +66,9 @@ export class AuthModule {
LoggerService,
IFrameService,
EqualityService,
LoginService,
AuthWellKnownDataService,
AuthWellKnownService,
DataService,
StateValidationService,
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ describe('Auth State Service', () => {

describe('hasIdTokenExpired', () => {
it('tokenValidationService gets called with id token if id_token is set', () => {
configurationProvider.setConfig({ renewTimeBeforeTokenExpiresInSeconds: 30 }, null);
configurationProvider.setConfig({ renewTimeBeforeTokenExpiresInSeconds: 30 });
const spy = spyOn(tokenValidationService, 'hasIdTokenExpired').and.callFake((a, b) => true);
spyOnProperty(storagePersistanceService, 'idToken', 'get').and.returnValue('idToken');
authStateService.hasIdTokenExpired();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { TestBed } from '@angular/core/testing';
import { of } from 'rxjs';
import { DataService } from '../api/data.service';
import { DataServiceMock } from '../api/data.service-mock';
import { AuthWellKnownDataService } from './auth-well-known-data.service';

describe('AuthWellKnownDataService', () => {
let service: AuthWellKnownDataService;
let dataService: DataService;

beforeEach(() => {
TestBed.configureTestingModule({
providers: [AuthWellKnownDataService, { provide: DataService, useClass: DataServiceMock }],
});
});

beforeEach(() => {
service = TestBed.inject(AuthWellKnownDataService);
dataService = TestBed.inject(DataService);
});

it('should create', () => {
expect(service).toBeTruthy();
});

describe('getWellKnownDocument', () => {
it('should add suffix if it does not exist on current url', () => {
const dataServiceSpy = spyOn(dataService, 'get').and.callFake((url) => {
return of(null);
});

const urlWithoutSuffix = 'myUrl';
const urlWithSuffix = `${urlWithoutSuffix}/.well-known/openid-configuration`;
service.getWellKnownDocument(urlWithoutSuffix);
expect(dataServiceSpy).toHaveBeenCalledWith(urlWithSuffix);
});

it('should not add suffix if it does exist on current url', () => {
const dataServiceSpy = spyOn(dataService, 'get').and.callFake((url) => {
return of(null);
});

const urlWithSuffix = `myUrl/.well-known/openid-configuration`;
service.getWellKnownDocument(urlWithSuffix);
expect(dataServiceSpy).toHaveBeenCalledWith(urlWithSuffix);
});

it('should not add suffix if it does exist in the middle of current url', () => {
const dataServiceSpy = spyOn(dataService, 'get').and.callFake((url) => {
return of(null);
});

const urlWithSuffix = `myUrl/.well-known/openid-configuration/and/some/more/stuff`;
service.getWellKnownDocument(urlWithSuffix);
expect(dataServiceSpy).toHaveBeenCalledWith(urlWithSuffix);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Injectable } from '@angular/core';
import { DataService } from '../api/data.service';

@Injectable()
export class AuthWellKnownDataService {
private WELL_KNOWN_SUFFIX = `/.well-known/openid-configuration`;

constructor(private readonly http: DataService) {}

getWellKnownDocument(wellKnownEndpoint: string) {
let url = wellKnownEndpoint;

if (!wellKnownEndpoint.includes(this.WELL_KNOWN_SUFFIX)) {
url = `${wellKnownEndpoint}${this.WELL_KNOWN_SUFFIX}`;
}

return this.http.get<any>(url);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Injectable } from '@angular/core';
import { of } from 'rxjs';

@Injectable()
export class AuthWellKnownServiceMock {
getWellKnownEndPointsFromUrl(authWellknownEndpoint: string) {
return of();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Injectable } from '@angular/core';
import { map } from 'rxjs/operators';
import { AuthWellKnownDataService } from './auth-well-known-data.service';
import { AuthWellKnownEndpoints } from './auth-well-known-endpoints';

@Injectable()
export class AuthWellKnownService {
constructor(private dataService: AuthWellKnownDataService) {}

getWellKnownEndPointsFromUrl(authWellknownEndpoint: string) {
return this.dataService.getWellKnownDocument(authWellknownEndpoint).pipe(
map((wellKnownEndpoints) => {
return {
issuer: wellKnownEndpoints.issuer,
jwksUri: wellKnownEndpoints.jwks_uri,
authorizationEndpoint: wellKnownEndpoints.authorization_endpoint,
tokenEndpoint: wellKnownEndpoints.token_endpoint,
userinfoEndpoint: wellKnownEndpoints.userinfo_endpoint,
endSessionEndpoint: wellKnownEndpoints.end_session_endpoint,
checkSessionIframe: wellKnownEndpoints.check_session_iframe,
revocationEndpoint: wellKnownEndpoints.revocation_endpoint,
introspectionEndpoint: wellKnownEndpoints.introspection_endpoint,
} as AuthWellKnownEndpoints;
})
);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { AuthWellKnownEndpoints } from './auth-well-known-endpoints';
import { OpenIdConfiguration } from './openid-configuration';
import { PublicConfiguration } from './public-configuration';

Expand All @@ -7,10 +6,6 @@ export class ConfigurationProviderMock {
return null;
}

get wellKnownEndpoints(): AuthWellKnownEndpoints {
return null;
}

get configuration(): PublicConfiguration {
return null;
}
Expand All @@ -19,5 +14,5 @@ export class ConfigurationProviderMock {
return true;
}

setConfig(configuration: OpenIdConfiguration, wellKnownEndpoints: AuthWellKnownEndpoints) {}
setConfig(configuration: OpenIdConfiguration) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('ConfigurationProviderTests', () => {
});

it('setup defines openIDConfiguration', () => {
configurationProvider.setConfig({ stsServer: 'hello' }, null);
configurationProvider.setConfig({ stsServer: 'hello' });

expect(configurationProvider.openIDConfiguration).toBeDefined();
});
Expand All @@ -35,26 +35,6 @@ describe('ConfigurationProviderTests', () => {
expect(configurationProvider.openIDConfiguration).toBeNull();
});

it('setup defines authwellknownendpoints', () => {
const toPass = {
issuer: '',
jwks_uri: '',
authorization_endpoint: '',
token_endpoint: '',
userinfo_endpoint: '',
end_session_endpoint: '',
check_session_iframe: '',
revocation_endpoint: '',
introspection_endpoint: '',
};

const expected = { ...toPass };

configurationProvider.setConfig(null, toPass);

expect(configurationProvider.wellKnownEndpoints).toEqual(expected);
});

it('setup defines default openIDConfiguration', () => {
const defaultConfig: OpenIdConfiguration = {
stsServer: 'https://please_set',
Expand Down Expand Up @@ -84,10 +64,11 @@ describe('ConfigurationProviderTests', () => {
disableIatOffsetValidation: false,
storage: sessionStorage,
customParams: {},
eagerLoadAuthWellKnownEndpoints: true,
disableRefreshIdTokenAuthTimeValidation: false,
};

configurationProvider.setConfig({ stsServer: 'https://please_set' }, null);
configurationProvider.setConfig({ stsServer: 'https://please_set' });

expect(configurationProvider.openIDConfiguration).toEqual(defaultConfig);
});
Expand Down Expand Up @@ -125,10 +106,11 @@ describe('ConfigurationProviderTests', () => {
disableIatOffsetValidation: false,
storage: sessionStorage,
customParams: {},
eagerLoadAuthWellKnownEndpoints: true,
disableRefreshIdTokenAuthTimeValidation: false,
};

configurationProvider.setConfig(config, null);
configurationProvider.setConfig(config);

expect(configurationProvider.openIDConfiguration).toEqual(expected);
});
Expand Down Expand Up @@ -168,12 +150,13 @@ describe('ConfigurationProviderTests', () => {
disableIatOffsetValidation: false,
storage: sessionStorage,
customParams: {},
eagerLoadAuthWellKnownEndpoints: true,
disableRefreshIdTokenAuthTimeValidation: false,
};

spyOnProperty(platformProvider, 'isBrowser').and.returnValue(false);

configurationProvider.setConfig(config, null);
configurationProvider.setConfig(config);

expect(configurationProvider.openIDConfiguration).toEqual(expected);
});
Expand All @@ -188,7 +171,7 @@ describe('ConfigurationProviderTests', () => {

const spy = spyOn(configurationProvider as any, 'setSpecialCases');

configurationProvider.setConfig(config, null);
configurationProvider.setConfig(config);

expect(spy).toHaveBeenCalled();
});
Expand All @@ -203,7 +186,7 @@ describe('ConfigurationProviderTests', () => {

spyOnProperty(platformProvider, 'isBrowser').and.returnValue(true);

configurationProvider.setConfig(config, null);
configurationProvider.setConfig(config);

expect(configurationProvider.openIDConfiguration.silentRenew).toEqual(true);
expect(configurationProvider.openIDConfiguration.startCheckSession).toEqual(true);
Expand All @@ -219,42 +202,18 @@ describe('ConfigurationProviderTests', () => {

spyOnProperty(platformProvider, 'isBrowser').and.returnValue(false);

configurationProvider.setConfig(config, null);
configurationProvider.setConfig(config);

expect(configurationProvider.openIDConfiguration.silentRenew).toEqual(false);
expect(configurationProvider.openIDConfiguration.startCheckSession).toEqual(false);
});

it('asking for public config returns null when internal configs are both not set', () => {
configurationProvider.setConfig(null, null);

const currentConfig = configurationProvider.configuration;

expect(currentConfig).toBeNull();
});

it('public config has default config if config is not getting passed', () => {
configurationProvider.setConfig(null, {});

const currentConfig = configurationProvider.configuration;

expect(currentConfig).not.toBeNull();
expect(currentConfig.configuration).toEqual(DEFAULT_CONFIG);
});

it('asking for public config returns null when internal configs if wellknown is not set', () => {
configurationProvider.setConfig({}, null);

const currentConfig = configurationProvider.configuration;

expect(currentConfig).toBeNull();
});

it('asking for public config returns config when internal configs are both set', () => {
configurationProvider.setConfig({}, {});
configurationProvider.setConfig(null);

const currentConfig = configurationProvider.configuration;
const currentConfig = configurationProvider.openIDConfiguration;

expect(currentConfig).not.toBeNull();
expect(currentConfig).toEqual(DEFAULT_CONFIG);
});
});
Loading