Browse Source

make some improvements for auth strategies

pull/7861/head
mehmet-erim 6 years ago
parent
commit
bed3635eab
  1. 2
      npm/ng-packs/apps/dev-app/src/app/home/home.component.ts
  2. 2
      npm/ng-packs/packages/account/src/lib/account.module.ts
  3. 4
      npm/ng-packs/packages/account/src/lib/components/login/login.component.html
  4. 26
      npm/ng-packs/packages/account/src/lib/components/login/login.component.ts
  5. 24
      npm/ng-packs/packages/account/src/lib/components/register/register.component.ts
  6. 2
      npm/ng-packs/packages/account/src/lib/guards/authentication-flow.guard.ts
  7. 55
      npm/ng-packs/packages/account/src/lib/services/authentication.service.ts
  8. 1
      npm/ng-packs/packages/account/src/lib/services/index.ts
  9. 9
      npm/ng-packs/packages/account/src/lib/utils/auth-utils.ts
  10. 1
      npm/ng-packs/packages/account/src/lib/utils/index.ts
  11. 2
      npm/ng-packs/packages/core/src/lib/core.module.ts
  12. 2
      npm/ng-packs/packages/core/src/lib/guards/auth.guard.ts
  13. 30
      npm/ng-packs/packages/core/src/lib/services/auth-flow-initializer.ts
  14. 50
      npm/ng-packs/packages/core/src/lib/services/auth.service.ts
  15. 1
      npm/ng-packs/packages/core/src/lib/services/index.ts
  16. 85
      npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts
  17. 1
      npm/ng-packs/packages/core/src/lib/strategies/index.ts
  18. 6
      npm/ng-packs/packages/core/src/lib/tests/auth.guard.spec.ts
  19. 6
      npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts
  20. 6
      npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/current-user.component.ts
  21. 2
      npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts
  22. 2
      templates/app/angular/src/app/home/home.component.ts

2
npm/ng-packs/apps/dev-app/src/app/home/home.component.ts

@ -14,6 +14,6 @@ export class HomeComponent {
constructor(private oAuthService: OAuthService, private authService: AuthService) {}
login() {
this.authService.initLogin();
this.authService.navigateToLogin();
}
}

2
npm/ng-packs/packages/account/src/lib/account.module.ts

@ -15,7 +15,6 @@ import { AccountConfigOptions } from './models/config-options';
import { ACCOUNT_CONFIG_OPTIONS } from './tokens/config-options.token';
import { accountConfigOptionsFactory } from './utils/factory-utils';
import { AuthenticationFlowGuard } from './guards/authentication-flow.guard';
import { AuthenticationService } from './services/authentication.service';
import { ForgotPasswordComponent } from './components/forgot-password/forgot-password.component';
import { ResetPasswordComponent } from './components/reset-password/reset-password.component';
@ -48,7 +47,6 @@ export class AccountModule {
ngModule: AccountModule,
providers: [
AuthenticationFlowGuard,
AuthenticationService,
{ provide: ACCOUNT_CONFIG_OPTIONS, useValue: options },
{
provide: 'ACCOUNT_OPTIONS',

4
npm/ng-packs/packages/account/src/lib/components/login/login.component.html

@ -1,7 +1,7 @@
<h4>{{ 'AbpAccount::Login' | abpLocalization }}</h4>
<strong *ngIf="isSelfRegistrationEnabled">
{{ 'AbpAccount::AreYouANewUser' | abpLocalization }}
<a class="text-decoration-none" routerLink="/account/register">{{
<a class="text-decoration-none" routerLink="/account/register" queryParamsHandling="preserve">{{
'AbpAccount::Register' | abpLocalization
}}</a>
</strong>
@ -37,7 +37,7 @@
class="form-check-input"
type="checkbox"
id="login-input-remember-me"
formControlName="remember"
formControlName="rememberMe"
/>
{{ 'AbpAccount::RememberMe' | abpLocalization }}
</label>

26
npm/ng-packs/packages/account/src/lib/components/login/login.component.ts

@ -1,13 +1,13 @@
import { ConfigStateService } from '@abp/ng.core';
import { ConfigStateService, AuthService } from '@abp/ng.core';
import { ToasterService } from '@abp/ng.theme.shared';
import { Component, OnInit } from '@angular/core';
import { Component, Injector, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngxs/store';
import { throwError } from 'rxjs';
import { catchError, finalize } from 'rxjs/operators';
import snq from 'snq';
import { eAccountComponents } from '../../enums/components';
import { AuthenticationService } from '../../services/authentication.service';
import { getRedirectUrl } from '../../utils/auth-utils';
const { maxLength, required } = Validators;
@ -25,11 +25,11 @@ export class LoginComponent implements OnInit {
authWrapperKey = eAccountComponents.AuthWrapper;
constructor(
private fb: FormBuilder,
private store: Store,
private toasterService: ToasterService,
private authenticationService: AuthenticationService,
private configState: ConfigStateService,
protected injector: Injector,
protected fb: FormBuilder,
protected toasterService: ToasterService,
protected authService: AuthService,
protected configState: ConfigStateService,
) {}
ngOnInit() {
@ -48,7 +48,7 @@ export class LoginComponent implements OnInit {
this.form = this.fb.group({
username: ['', [required, maxLength(255)]],
password: ['', [required, maxLength(128)]],
remember: [false],
rememberMe: [false],
});
}
@ -57,10 +57,12 @@ export class LoginComponent implements OnInit {
this.inProgress = true;
const { username, password, remember } = this.form.value;
const { username, password, rememberMe } = this.form.value;
this.authenticationService
.login(username, password, remember)
const redirectUrl = getRedirectUrl(this.injector);
this.authService
.login({ username, password, rememberMe, redirectUrl })
.pipe(
catchError(err => {
this.toasterService.error(

24
npm/ng-packs/packages/account/src/lib/components/register/register.component.ts

@ -1,4 +1,4 @@
import { ConfigStateService } from '@abp/ng.core';
import { AuthService, ConfigStateService } from '@abp/ng.core';
import { getPasswordValidators, ToasterService } from '@abp/ng.theme.shared';
import { Component, Injector, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@ -8,7 +8,7 @@ import snq from 'snq';
import { eAccountComponents } from '../../enums/components';
import { AccountService } from '../../proxy/account/account.service';
import { RegisterDto } from '../../proxy/account/models';
import { AuthenticationService } from '../../services';
import { getRedirectUrl } from '../../utils/auth-utils';
const { maxLength, required, email } = Validators;
@Component({
@ -25,12 +25,12 @@ export class RegisterComponent implements OnInit {
authWrapperKey = eAccountComponents.AuthWrapper;
constructor(
private fb: FormBuilder,
private accountService: AccountService,
private configState: ConfigStateService,
private toasterService: ToasterService,
private authenticationSerivice: AuthenticationService,
private injector: Injector,
protected fb: FormBuilder,
protected accountService: AccountService,
protected configState: ConfigStateService,
protected toasterService: ToasterService,
protected authService: AuthService,
protected injector: Injector,
) {}
ngOnInit() {
@ -79,7 +79,13 @@ export class RegisterComponent implements OnInit {
this.accountService
.register(newUser)
.pipe(
switchMap(() => this.authenticationSerivice.login(newUser.userName, newUser.password)),
switchMap(() =>
this.authService.login({
username: newUser.userName,
password: newUser.password,
redirectUrl: getRedirectUrl(this.injector),
}),
),
catchError(err => {
this.toasterService.error(
snq(() => err.error.error_description) ||

2
npm/ng-packs/packages/account/src/lib/guards/authentication-flow.guard.ts

@ -9,7 +9,7 @@ export class AuthenticationFlowGuard implements CanActivate {
canActivate() {
if (this.authService.isInternalAuth) return true;
this.authService.initLogin();
this.authService.navigateToLogin();
return false;
}
}

55
npm/ng-packs/packages/account/src/lib/services/authentication.service.ts

@ -1,55 +0,0 @@
import {
AbpApplicationConfigurationService,
AuthPasswordFlowStrategy,
AuthService,
ConfigStateService,
RestService,
SessionStateService,
} from '@abp/ng.core';
import { HttpHeaders } from '@angular/common/http';
import { Inject, Injectable } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { OAuthService } from 'angular-oauth2-oidc';
import { from, Observable } from 'rxjs';
import { switchMap, take, tap } from 'rxjs/operators';
import { AccountConfigOptions } from '../models/config-options';
import { ACCOUNT_CONFIG_OPTIONS } from '../tokens/config-options.token';
@Injectable()
export class AuthenticationService {
constructor(
protected rest: RestService,
protected sessionState: SessionStateService,
protected authService: AuthService,
protected oAuthService: OAuthService,
protected appConfigService: AbpApplicationConfigurationService,
protected configState: ConfigStateService,
@Inject(ACCOUNT_CONFIG_OPTIONS) protected options: AccountConfigOptions,
protected router: Router,
protected route: ActivatedRoute,
) {}
login(username: string, password: string, remember = false): Observable<any> {
const tenant = this.sessionState.getTenant();
return from(
this.oAuthService.fetchTokenUsingPasswordFlow(
username,
password,
new HttpHeaders({ ...(tenant && tenant.id && { __tenant: tenant.id }) }),
),
).pipe(
switchMap(() => this.appConfigService.get()),
tap(res => {
this.configState.setState(res);
const redirectUrl =
this.route.snapshot.queryParams.returnUrl || this.options.redirectUrl || '/';
this.router.navigate([redirectUrl]);
this.authService.strategy.setRememberMe(remember);
}),
);
}
}

1
npm/ng-packs/packages/account/src/lib/services/index.ts

@ -1,2 +1 @@
export * from './authentication.service';
export * from './manage-profile.state.service';

9
npm/ng-packs/packages/account/src/lib/utils/auth-utils.ts

@ -0,0 +1,9 @@
import { Injector } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ACCOUNT_CONFIG_OPTIONS } from '../tokens';
export function getRedirectUrl(injector: Injector) {
const route = injector.get(ActivatedRoute);
const options = injector.get(ACCOUNT_CONFIG_OPTIONS);
return route.snapshot.queryParams.returnUrl || options.redirectUrl || '/';
}

1
npm/ng-packs/packages/account/src/lib/utils/index.ts

@ -1 +1,2 @@
export * from './auth-utils';
export * from './factory-utils';

2
npm/ng-packs/packages/core/src/lib/core.module.ts

@ -26,7 +26,6 @@ import { ABP } from './models/common';
import { LocalizationPipe } from './pipes/localization.pipe';
import { SortPipe } from './pipes/sort.pipe';
import { LocaleProvider } from './providers/locale.provider';
import { AuthFlowInitializer } from './services/auth-flow-initializer';
import { LocalizationService } from './services/localization.service';
import { ProfileState } from './states/profile.state';
import { oAuthStorage } from './strategies/auth-flow.strategy';
@ -179,7 +178,6 @@ export class CoreModule {
useFactory: noop,
},
{ provide: OAuthStorage, useFactory: storageFactory },
AuthFlowInitializer,
],
};
}

2
npm/ng-packs/packages/core/src/lib/guards/auth.guard.ts

@ -16,7 +16,7 @@ export class AuthGuard implements CanActivate {
return true;
}
this.authService.initLogin();
this.authService.navigateToLogin();
return false;
}
}

30
npm/ng-packs/packages/core/src/lib/services/auth-flow-initializer.ts

@ -1,30 +0,0 @@
import { Injectable, Injector } from '@angular/core';
import { from, Observable } from 'rxjs';
import { filter, map, switchMap, take, tap } from 'rxjs/operators';
import { AUTH_FLOW_STRATEGY } from '../strategies/auth-flow.strategy';
import { AuthService } from './auth.service';
import { EnvironmentService } from './environment.service';
@Injectable()
export class AuthFlowInitializer {
constructor(
protected injector: Injector,
protected environmentService: EnvironmentService,
protected authService: AuthService,
) {}
init(): Observable<any> | Promise<any> {
return this.environmentService.getEnvironment$().pipe(
map(env => env?.oAuthConfig),
filter(oAuthConfig => !!oAuthConfig),
tap(oAuthConfig => {
this.authService.strategy =
oAuthConfig.responseType === 'code'
? AUTH_FLOW_STRATEGY.Code(this.injector)
: AUTH_FLOW_STRATEGY.Password(this.injector);
}),
switchMap(() => from(this.authService.init())),
take(1),
);
}
}

50
npm/ng-packs/packages/core/src/lib/services/auth.service.ts

@ -1,10 +1,10 @@
import { Injectable, Injector } from '@angular/core';
import { Observable } from 'rxjs';
import { from, Observable } from 'rxjs';
import { filter, map, switchMap, take, tap } from 'rxjs/operators';
import {
AuthCodeFlowStrategy,
AuthFlowStrategy,
AuthPasswordFlowStrategy,
AUTH_FLOW_STRATEGY,
LoginParams,
} from '../strategies/auth-flow.strategy';
import { EnvironmentService } from './environment.service';
@ -12,30 +12,50 @@ import { EnvironmentService } from './environment.service';
providedIn: 'root',
})
export class AuthService {
private _strategy: AuthFlowStrategy;
set strategy(strategy: AuthFlowStrategy) {
if (this.strategy) this.strategy.destroy();
this._strategy = strategy;
}
get strategy() {
return this._strategy;
}
private strategy: AuthFlowStrategy;
get isInternalAuth() {
return this.strategy.isInternalAuth;
}
constructor(protected injector: Injector) {}
async init() {
return await this.strategy.init();
const environmentService = this.injector.get(EnvironmentService);
return environmentService
.getEnvironment$()
.pipe(
map(env => env?.oAuthConfig),
filter(oAuthConfig => !!oAuthConfig),
tap(oAuthConfig => {
this.strategy =
oAuthConfig.responseType === 'code'
? AUTH_FLOW_STRATEGY.Code(this.injector)
: AUTH_FLOW_STRATEGY.Password(this.injector);
}),
switchMap(() => from(this.strategy.init())),
take(1),
)
.toPromise();
}
logout(): Observable<any> {
return this.strategy.logout();
}
/**
* @deprecated Use navigateToLogin method instead. To be deleted in v5.0
*/
initLogin() {
this.strategy.login();
this.strategy.navigateToLogin();
}
navigateToLogin() {
this.strategy.navigateToLogin();
}
login(params: LoginParams) {
return this.strategy.login(params);
}
}

1
npm/ng-packs/packages/core/src/lib/services/index.ts

@ -1,6 +1,5 @@
export * from './application-configuration.service';
export * from './auth.service';
export * from './auth-flow-initializer';
export * from './config-state.service';
export * from './content-projection.service';
export * from './dom-insertion.service';

85
npm/ng-packs/packages/core/src/lib/strategies/auth-flow.strategy.ts

@ -1,3 +1,4 @@
import { HttpHeaders } from '@angular/common/http';
import { Injector } from '@angular/core';
import { Router } from '@angular/router';
import { Store } from '@ngxs/store';
@ -6,9 +7,16 @@ import { from, Observable, of } from 'rxjs';
import { filter, switchMap, tap } from 'rxjs/operators';
import { RestOccurError } from '../actions/rest.actions';
import { AbpApplicationConfigurationService } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-configuration.service';
import { SessionStateService } from '../services/session-state.service';
import { ConfigStateService } from '../services/config-state.service';
import { EnvironmentService } from '../services/environment.service';
import { RestService } from '../services/rest.service';
export interface LoginParams {
username: string;
password: string;
rememberMe?: boolean;
redirectUrl?: string;
}
export const oAuthStorage = localStorage;
@ -21,9 +29,9 @@ export abstract class AuthFlowStrategy {
protected oAuthService: OAuthService;
protected oAuthConfig: AuthConfig;
abstract checkIfInternalAuth(): boolean;
abstract login(): void;
abstract navigateToLogin(): void;
abstract logout(): Observable<any>;
abstract destroy(): void;
abstract login(params?: LoginParams): Observable<any>;
private catchError = err => this.store.dispatch(new RestOccurError(err));
@ -54,8 +62,6 @@ export abstract class AuthFlowStrategy {
})
.catch(this.catchError);
}
setRememberMe(remember: boolean) {}
}
export class AuthCodeFlowStrategy extends AuthFlowStrategy {
@ -68,7 +74,7 @@ export class AuthCodeFlowStrategy extends AuthFlowStrategy {
.then(() => this.oAuthService.setupAutomaticSilentRefresh({}, 'access_token'));
}
login() {
navigateToLogin() {
this.oAuthService.initCodeFlow();
}
@ -81,7 +87,10 @@ export class AuthCodeFlowStrategy extends AuthFlowStrategy {
return from(this.oAuthService.revokeTokenAndLogout());
}
destroy() {}
login() {
this.oAuthService.initCodeFlow();
return of(null);
}
}
export class AuthPasswordFlowStrategy extends AuthFlowStrategy {
@ -112,11 +121,20 @@ export class AuthPasswordFlowStrategy extends AuthFlowStrategy {
});
}
async init() {
this.oAuthService.events.pipe(filter(event => event.type === 'logout')).subscribe(() => {
this.removeRememberMe();
});
private setRememberMe(remember: boolean) {
this.removeRememberMe();
localStorage.setItem(this.storageKey, 'true');
document.cookie = `${this.cookieKey}=true${
remember ? ';expires=Fri, 31 Dec 9999 23:59:59 GMT' : ''
}`;
}
private removeRememberMe() {
localStorage.removeItem(this.storageKey);
document.cookie = this.cookieKey + '= ; expires = Thu, 01 Jan 1970 00:00:00 GMT';
}
async init() {
if (!getCookieValueByName('rememberMe') && localStorage.getItem(this.storageKey)) {
this.oAuthService.logOut();
}
@ -124,7 +142,7 @@ export class AuthPasswordFlowStrategy extends AuthFlowStrategy {
return super.init().then(() => this.listenToTokenExpiration());
}
login() {
navigateToLogin() {
const router = this.injector.get(Router);
router.navigateByUrl('/account/login');
}
@ -133,29 +151,38 @@ export class AuthPasswordFlowStrategy extends AuthFlowStrategy {
return true;
}
logout() {
login(params: LoginParams): Observable<any> {
const sessionState = this.injector.get(SessionStateService);
const router = this.injector.get(Router);
return from(this.oAuthService.revokeTokenAndLogout()).pipe(
const tenant = sessionState.getTenant();
return from(
this.oAuthService.fetchTokenUsingPasswordFlow(
params.username,
params.password,
new HttpHeaders({ ...(tenant && tenant.id && { __tenant: tenant.id }) }),
),
).pipe(
switchMap(() => this.appConfigService.get()),
tap(res => this.configState.setState(res)),
tap(() => router.navigateByUrl('/')),
tap(res => {
this.configState.setState(res);
this.setRememberMe(params.rememberMe);
router.navigate([params.redirectUrl || '/']);
}),
);
}
destroy() {}
setRememberMe(remember: boolean) {
this.removeRememberMe();
localStorage.setItem(this.storageKey, 'true');
document.cookie = `${this.cookieKey}=true${
remember ? ';expires=Fri, 31 Dec 9999 23:59:59 GMT' : ''
}`;
}
logout() {
const router = this.injector.get(Router);
removeRememberMe() {
localStorage.removeItem(this.storageKey);
document.cookie = this.cookieKey + '= ; expires = Thu, 01 Jan 1970 00:00:00 GMT';
return from(this.oAuthService.revokeTokenAndLogout()).pipe(
switchMap(() => this.appConfigService.get()),
tap(res => {
this.configState.setState(res);
router.navigateByUrl('/');
this.removeRememberMe();
}),
);
}
}

1
npm/ng-packs/packages/core/src/lib/strategies/index.ts

@ -1,4 +1,3 @@
export * from './auth-flow.strategy';
export * from './container.strategy';
export * from './content-security.strategy';
export * from './content.strategy';

6
npm/ng-packs/packages/core/src/lib/tests/auth.guard.spec.ts

@ -21,12 +21,12 @@ describe('AuthGuard', () => {
expect(guard.canActivate()).toBe(true);
});
it('should execute the initLogin method of the authService', () => {
it('should execute the navigateToLogin method of the authService', () => {
const authService = spectator.inject(AuthService);
spectator.inject(OAuthService).hasValidAccessToken.andReturn(false);
const initLoginSpy = jest.spyOn(authService, 'initLogin');
const navigateToLoginSpy = jest.spyOn(authService, 'navigateToLogin');
expect(guard.canActivate()).toBe(false);
expect(initLoginSpy).toHaveBeenCalled();
expect(navigateToLoginSpy).toHaveBeenCalled();
});
});

6
npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts

@ -1,13 +1,12 @@
import { registerLocaleData } from '@angular/common';
import { Injector } from '@angular/core';
import { OAuthService } from 'angular-oauth2-oidc';
import { isObservable, Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { ABP } from '../models/common';
import { Environment } from '../models/environment';
import { AbpApplicationConfigurationService } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-configuration.service';
import { CurrentTenantDto } from '../proxy/volo/abp/asp-net-core/mvc/multi-tenancy/models';
import { AuthFlowInitializer } from '../services/auth-flow-initializer';
import { AuthService } from '../services';
import { ConfigStateService } from '../services/config-state.service';
import { EnvironmentService } from '../services/environment.service';
import { SessionStateService } from '../services/session-state.service';
@ -26,8 +25,7 @@ export function getInitialData(injector: Injector) {
environmentService.setState(options.environment as Environment);
await getRemoteEnv(injector, options.environment);
await parseTenantFromUrl(injector);
const authFlowInit = injector.get(AuthFlowInitializer).init();
await (isObservable(authFlowInit) ? authFlowInit.toPromise() : authFlowInit);
await injector.get(AuthService).init();
if (options.skipGetAppConfiguration) return;

6
npm/ng-packs/packages/theme-basic/src/lib/components/nav-items/current-user.component.ts

@ -7,7 +7,7 @@ import { Observable } from 'rxjs';
// tslint:disable-next-line: component-max-inline-declarations
template: `
<ng-template #loginBtn>
<a role="button" class="nav-link pointer" (click)="initLogin()">{{
<a role="button" class="nav-link pointer" (click)="navigateToLogin()">{{
'AbpAccount::Login' | abpLocalization
}}</a>
</ng-template>
@ -54,8 +54,8 @@ export class CurrentUserComponent {
constructor(private authService: AuthService, private configState: ConfigStateService) {}
initLogin() {
this.authService.initLogin();
navigateToLogin() {
this.authService.navigateToLogin();
}
logout() {

2
npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts

@ -257,7 +257,7 @@ export class ErrorHandler {
}
private navigateToLogin() {
this.injector.get(AuthService).initLogin();
this.injector.get(AuthService).navigateToLogin();
}
createErrorComponent(instance: Partial<HttpErrorWrapperComponent>) {

2
templates/app/angular/src/app/home/home.component.ts

@ -14,6 +14,6 @@ export class HomeComponent {
constructor(private oAuthService: OAuthService, private authService: AuthService) {}
login() {
this.authService.initLogin();
this.authService.navigateToLogin();
}
}

Loading…
Cancel
Save