Browse Source

Auth improved.

pull/65/merge
Sebastian Stehle 9 years ago
parent
commit
5b5bc57375
  1. 20
      src/Squidex/app/shared/interceptors/auth.interceptor.spec.ts
  2. 52
      src/Squidex/app/shared/interceptors/auth.interceptor.ts
  3. 30
      src/Squidex/app/shared/services/auth.service.ts

20
src/Squidex/app/shared/interceptors/auth.interceptor.spec.ts

@ -104,7 +104,25 @@ describe('AuthInterceptor', () => {
authService.verify(x => x.logoutRedirect(), Times.once()); authService.verify(x => x.logoutRedirect(), Times.once());
})); }));
[401, 403].forEach(statusCode => { it(`should logout for 401 status code`,
inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => {
authService.setup(x => x.userChanges).returns(() => { return Observable.of(<any>{ authToken: 'letmein' }); });
authService.setup(x => x.loginSilent()).returns(() => { return Observable.of(<any>{ authToken: 'letmereallyin' }); });
http.get('http://service/p/apps').subscribe(
_ => { /* NOOP */ },
_ => { /* NOOP */ });
// const req = httpMock.expectOne('http://service/p/apps');
httpMock.expectOne('http://service/p/apps').error(<any>{}, { status: 401 });
httpMock.expectOne('http://service/p/apps').error(<any>{}, { status: 401 });
authService.verify(x => x.logoutRedirect(), Times.once());
}));
[403].forEach(statusCode => {
it(`should logout for ${statusCode} status code`, it(`should logout for ${statusCode} status code`,
inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => { inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => {

52
src/Squidex/app/shared/interceptors/auth.interceptor.ts

@ -9,7 +9,7 @@ import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse
import { Injectable} from '@angular/core'; import { Injectable} from '@angular/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { AuthService } from './../services/auth.service'; import { AuthService, Profile } from './../services/auth.service';
import { ApiUrlConfig } from 'framework'; import { ApiUrlConfig } from 'framework';
@Injectable() @Injectable()
@ -25,31 +25,37 @@ export class AuthInterceptor implements HttpInterceptor {
public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.url.indexOf(this.baseUrl) === 0 && !req.headers.has('NoAuth')) { if (req.url.indexOf(this.baseUrl) === 0 && !req.headers.has('NoAuth')) {
return this.authService.userChanges.first().switchMap(user => { return this.authService.userChanges.first().switchMap(user => {
const token = user ? user.authToken : ''; return this.makeRequest(req, next, user, true);
const authReq = req.clone({
headers: req.headers
.set('Authorization', token)
.set('Accept-Language', '*')
.set('Pragma', 'no-cache')
});
return next.handle(authReq)
.catch((error: HttpErrorResponse) => {
if (error.status === 404 && (!user || user.isExpired)) {
this.authService.logoutRedirect();
return Observable.empty<Response>();
} else if (error.status === 401 || error.status === 403) {
this.authService.logoutRedirect();
return Observable.empty<Response>();
}
return Observable.throw(error);
});
}); });
} else { } else {
return next.handle(req); return next.handle(req);
} }
} }
private makeRequest(req: HttpRequest<any>, next: HttpHandler, user: Profile, renew = false): Observable<HttpEvent<any>> {
const token = user ? user.authToken : '';
const authReq = req.clone({
headers: req.headers
.set('Authorization', token)
.set('Accept-Language', '*')
.set('Pragma', 'no-cache')
});
return next.handle(authReq)
.catch((error: HttpErrorResponse) => {
if (error.status === 401 && renew) {
return this.authService.loginSilent().switchMap(u => this.makeRequest(req, next, u));
} else if (error.status === 404 && (!user || user.isExpired)) {
this.authService.logoutRedirect();
return Observable.empty<Response>();
}else if (error.status === 401 || error.status === 403) {
this.authService.logoutRedirect();
return Observable.empty<Response>();
}
return Observable.throw(error);
});
}
} }

30
src/Squidex/app/shared/services/auth.service.ts

@ -11,7 +11,8 @@ import { Observable, ReplaySubject } from 'rxjs';
import { import {
Log, Log,
User, User,
UserManager UserManager,
WebStorageStateStore
} from 'oidc-client'; } from 'oidc-client';
import { ApiUrlConfig } from 'framework'; import { ApiUrlConfig } from 'framework';
@ -81,6 +82,7 @@ export class AuthService {
silent_redirect_uri: apiUrl.buildUrl('identity-server/client-callback-silent/'), silent_redirect_uri: apiUrl.buildUrl('identity-server/client-callback-silent/'),
popup_redirect_uri: apiUrl.buildUrl('identity-server/client-callback-popup/'), popup_redirect_uri: apiUrl.buildUrl('identity-server/client-callback-popup/'),
authority: apiUrl.buildUrl('identity-server/'), authority: apiUrl.buildUrl('identity-server/'),
userStore: new WebStorageStateStore({ store: window.localStorage || window.sessionStorage }),
automaticSilentRenew: true automaticSilentRenew: true
}); });
@ -96,7 +98,7 @@ export class AuthService {
this.currentUser = user; this.currentUser = user;
}); });
this.checkState(this.userManager.signinSilent()); this.checkState(this.userManager.getUser());
} }
public logoutRedirect(): Observable<any> { public logoutRedirect(): Observable<any> {
@ -107,20 +109,32 @@ export class AuthService {
return Observable.fromPromise(this.userManager.signoutRedirectCallback()); return Observable.fromPromise(this.userManager.signoutRedirectCallback());
} }
public loginPopup(): Observable<any> { public loginPopup(): Observable<Profile> {
return Observable.fromPromise(this.userManager.signinPopup()); return Observable.fromPromise(this.userManager.signinPopup()).map(u => this.createProfile(u));
}
public loginSilent(): Observable<any> {
return Observable.fromPromise(this.userManager.signinSilent()).map(u => this.createProfile(u));
} }
public loginRedirect(): Observable<any> { public loginRedirect(): Observable<any> {
return Observable.fromPromise(this.userManager.signinRedirect()); return Observable.fromPromise(this.userManager.signinRedirect());
} }
public loginRedirectComplete(): Observable<any> { public loginRedirectComplete(): Observable<Profile> {
return Observable.fromPromise(this.userManager.signinRedirectCallback()); return Observable.fromPromise(this.userManager.signinRedirectCallback()).map(u => this.createProfile(u));
} }
private checkState(promise: Promise<User>) { private createProfile(user: User) {
promise.catch((err) => { return user ? new Profile(user) : null;
}
private checkState(promise: Promise<User>) {
promise.then(user => {
this.user$.next(this.createProfile(user));
return true;
}, err => {
this.user$.next(null); this.user$.next(null);
return false; return false;

Loading…
Cancel
Save