Застрял в перенаправлении l oop с сервером идентификации с использованием потока проверки подлинности кода - PullRequest
0 голосов
/ 20 февраля 2020

У меня есть Angular SPA-приложение, которое я хочу интегрировать с сервером идентификации 4.

Я пытаюсь использовать новый рекомендуемый поток аутентификации, используя angular -auth-oid c -client library.

Я установил перехватчик Angular, который запускается при каждом запросе и пытается получить токен путем принудительного входа в систему, если пользователь не аутентифицирован. Его код такой:

import { HttpInterceptor, HttpRequest, HttpEvent, HttpHandler} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { AuthService } from '../authentication/auth.service';

@Injectable()
export class TokenInterceptorService implements HttpInterceptor {
constructor(private authService: AuthService) {}
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
      console.log('INTERCEPTOR' + this.authService);
      //// We retrieve the token, if any
      let token = this.authService.getToken();
      let newHeaders = req.headers;
      if (!token) {
        this.authService.login();
      }

      token = this.authService.getToken();
      newHeaders = newHeaders.append('bearer', token);
      //// Finally we have to clone our request with our new headers
      //// This is required because HttpRequests are immutable
      const authReq = req.clone({headers: newHeaders});
      //// Then we return an Observable that will run the request
      //// or pass it to the next interceptor if any
      return next.handle(authReq);
   }
}

Реализация AuthService выглядит следующим образом

import { Injectable, OnDestroy, Inject } from '@angular/core';
import { OidcSecurityService, OpenIdConfiguration, AuthWellKnownEndpoints, AuthorizationResult, AuthorizationState } from 'angular-auth-oidc-client';
import { Observable ,  Subscription, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { HttpHeaders, HttpClient, HttpParams } from '@angular/common/http';
import { Router } from '@angular/router';

@Injectable()
export class AuthService implements OnDestroy {

    isAuthorized = false;
    public bearerToken: string | null = null;
    public sessionState: number | null = null;

    constructor(
        private oidcSecurityService: OidcSecurityService,
        private http: HttpClient,
        private router: Router,
        @Inject('BASE_URL') private originUrl: string,
        @Inject('AUTH_URL') private authUrl: string,
    ) {
    }

    private isAuthorizedSubscription: Subscription = new Subscription;

    ngOnDestroy(): void {
        if (this.isAuthorizedSubscription) {
            this.isAuthorizedSubscription.unsubscribe();
        }
    }

    public initAuth() {
        const openIdConfiguration: OpenIdConfiguration = {
            stsServer: this.authUrl,
            redirect_url: this.originUrl + 'callback',
            client_id: 'myApp.api',
            response_type: 'code',
            scope: 'myApp.api',
            post_logout_redirect_uri: this.originUrl,
            forbidden_route: '/forbidden',
            unauthorized_route: '/unauthorized',
            silent_renew: true,
            silent_renew_url: this.originUrl,
            history_cleanup_off: true,
            auto_userinfo: true,
            log_console_warning_active: true,
            log_console_debug_active: true,
            max_id_token_iat_offset_allowed_in_seconds: 10,
        };

        const authWellKnownEndpoints: AuthWellKnownEndpoints = {
            issuer:'.office.sbs',
            jwks_uri: this.authUrl + '/.well-known/openid-configuration/jwks',
            authorization_endpoint: this.authUrl + '/connect/authorize',
            token_endpoint: this.authUrl + '/connect/token',
            userinfo_endpoint: this.authUrl + '/connect/userinfo',
            end_session_endpoint: this.authUrl + '/connect/endsession',
            check_session_iframe: this.authUrl + '/connect/checksession',
            revocation_endpoint: this.authUrl + '/connect/revocation',
            introspection_endpoint: this.authUrl + '/connect/introspect',
        };

        this.oidcSecurityService.setupModule(openIdConfiguration, authWellKnownEndpoints);

        if (this.oidcSecurityService.moduleSetup) {
            this.doCallbackLogicIfRequired();
        } else {
            this.oidcSecurityService.onModuleSetup.subscribe(() => {
                this.doCallbackLogicIfRequired();
            });
        }
        this.isAuthorizedSubscription = this.oidcSecurityService.getIsAuthorized().subscribe((isAuthorized => {
            this.isAuthorized = isAuthorized;
        }));

        this.oidcSecurityService.onAuthorizationResult.subscribe(
            (authorizationResult: AuthorizationResult) => {
                this.onAuthorizationResultComplete(authorizationResult);
            });
    }

    private onAuthorizationResultComplete(authorizationResult: AuthorizationResult) {

        console.log('Auth result received AuthorizationState:'
            + authorizationResult.authorizationState
            + ' validationResult:' + authorizationResult.validationResult);

        if (authorizationResult.authorizationState === AuthorizationState.unauthorized) {
            if (window.parent) {
                // sent from the child iframe, for example the silent renew
                this.router.navigate(['/unauthorized']);
            } else {
                window.location.href = '/unauthorized';
            }
        }
        else if (authorizationResult.authorizationState === AuthorizationState.authorized) {
            console.log("authenticated");
        }
    }

    private doCallbackLogicIfRequired() {

        //this.oidcSecurityService.authorizedCallbackWithCode(window.location.toString());

        console.log(window.location);

        const urlParts = window.location.toString().split('?');
        const params = new HttpParams({
            fromString: urlParts[1]
        });
        const code = params.get('code');
        const state = params.get('state');


        if (code && state && this.sessionState === null) {
            this.sessionState = Math.random() * 50000;
            this.oidcSecurityService.requestTokensWithCode(code, state, this.sessionState.toString());
        }
    }

    getIsAuthorized(): Observable<boolean> {
        return this.oidcSecurityService.getIsAuthorized();
    }

    login() {
        console.log('start login');
        this.oidcSecurityService.authorize();
    }

    logout() {
        console.log('start logoff');
        this.oidcSecurityService.logoff();
    }

    get(url: string): Observable<any> {
        return this.http.get(url, { headers: this.getHeaders() })
        .pipe(catchError((error) => {
            this.oidcSecurityService.handleError(error);
            return throwError(error);
        }));
    }

    put(url: string, data: any): Observable<any> {
        const body = JSON.stringify(data);
        return this.http.put(url, body, { headers: this.getHeaders() })
        .pipe(catchError((error) => {
            this.oidcSecurityService.handleError(error);
            return throwError(error);
        }));
    }

    delete(url: string): Observable<any> {
        return this.http.delete(url, { headers: this.getHeaders() })
        .pipe(catchError((error) => {
            this.oidcSecurityService.handleError(error);
            return throwError(error);
        }));
    }

    post(url: string, data: any): Observable<any> {
        const body = JSON.stringify(data);
        return this.http.post(url, body, { headers: this.getHeaders() })
        .pipe(catchError((error) => {
            this.oidcSecurityService.handleError(error);
            return throwError(error);
        }));
    }

    private getHeaders() {
        let headers = new HttpHeaders();
        headers = headers.set('Content-Type', 'application/json');
        return this.appendAuthHeader(headers);
    }

    public getToken() {
        const token = this.oidcSecurityService.getToken();

        return token;
    }

    private appendAuthHeader(headers: HttpHeaders) {
        const token = this.oidcSecurityService.getToken();

        if (token === '') { return headers; }

        const tokenValue = 'Bearer ' + token;
        return headers.set('Authorization', tokenValue);
    }
}

Основная проблема заключается в том, что когда я нажимаю на запрос с токеном в angular -auth- oid c -client. js (код ниже), запросы продолжают переворачиваться между сервером идентификации и страницей обратного вызова, и код продолжает выполнять метод doCallbackLogicIfRequired.

OidcSecurityService.prototype.requestTokensWithCodeProcedure$ = 
    // Code Flow with PCKE
    /**
     * @param {?} code
     * @param {?} state
     * @param {?} session_state
     * @return {?}
     */
    function (code, state, session_state) {
        var _this = this;
        /** @type {?} */
        var tokenRequestUrl = '';
        if (this.configurationProvider.wellKnownEndpoints && this.configurationProvider.wellKnownEndpoints.token_endpoint) {
            tokenRequestUrl = "" + this.configurationProvider.wellKnownEndpoints.token_endpoint;
        }
        if (!this.oidcSecurityValidation.validateStateFromHashCallback(state, this.oidcSecurityCommon.authStateControl)) {
            this.loggerService.logWarning('authorizedCallback incorrect state');
            // ValidationResult.StatesDoNotMatch;
            return throwError(new Error('incorrect state'));
        }
        /** @type {?} */
        var headers = new HttpHeaders();
        headers = headers.set('Content-Type', 'application/x-www-form-urlencoded');
        /** @type {?} */
        var data = oneLineTrim(templateObject_1 || (templateObject_1 = __makeTemplateObject(["grant_type=authorization_code&client_id=", "\n            &code_verifier=", "\n            &code=", "&redirect_uri=", ""], ["grant_type=authorization_code&client_id=", "\n            &code_verifier=", "\n            &code=", "&redirect_uri=", ""])), this.configurationProvider.openIDConfiguration.client_id, this.oidcSecurityCommon.code_verifier, code, this.configurationProvider.openIDConfiguration.redirect_url);
        if (this.oidcSecurityCommon.silentRenewRunning === 'running') {
            data = oneLineTrim(templateObject_2 || (templateObject_2 = __makeTemplateObject(["grant_type=authorization_code&client_id=", "\n                &code_verifier=", "\n                &code=", "\n                &redirect_uri=", ""], ["grant_type=authorization_code&client_id=", "\n                &code_verifier=", "\n                &code=", "\n                &redirect_uri=", ""])), this.configurationProvider.openIDConfiguration.client_id, this.oidcSecurityCommon.code_verifier, code, this.configurationProvider.openIDConfiguration.silent_renew_url);
        }
        return this.httpClient.post(tokenRequestUrl, data, { headers: headers }).pipe(map((/**
         * @param {?} response
         * @return {?}
         */
        function (response) {
            /** @type {?} */
            var obj = new Object();
            obj = response;
            obj.state = state;
            obj.session_state = session_state;
            _this.authorizedCodeFlowCallbackProcedure(obj);
            return undefined;
        })), catchError((/**
         * @param {?} error
         * @return {?}
         */
        function (error) {
            _this.loggerService.logError(error);
            _this.loggerService.logError("OidcService code request " + _this.configurationProvider.openIDConfiguration.stsServer);
            return throwError(error);
        })));
    };

Проверяя консоль Журнал, я вижу, я получил токен

VM1604 app.bundle.js:11364 INTERCEPTOR[object Object]
VM1604 app.bundle.js:1527 start login
VM1604 app.bundle.js:89392 BEGIN Authorize Code Flow, no auth data
VM1604 app.bundle.js:89392 AuthorizedController created. local state: 15821982471040.47561711387714610.5390697281765799
VM1604 app.bundle.js:75476 Angular is running in the development mode. Call enableProdMode() to enable the production mode.
VM1604 app.bundle.js:89392 BEGIN authorized Code Flow Callback, no auth data
VM1604 app.bundle.js:89392 history clean up inactive
VM1604 app.bundle.js:89392 {access_token: "eyJhbGciOiJSUzI1NiIsImtpZCI6IkJFODdGRjgwRjhBMkQyMT…V9-uKxgRq46JSiVpZbUSm_d7GB07aO1k4SvRWmIqw20Fo7ADA", expires_in: 3600, token_type: "Bearer", state: "15821982471040.47561711387714610.5390697281765799", session_state: "45871.95561094084"}
VM1604 app.bundle.js:89392 authorizedCallback created, begin token validation
VM1604 app.bundle.js:89392 jwks_uri: https://mySecurityMachine.myDomain/authentication/.well-known/openid-configuration/jwks
VM1604 app.bundle.js:11364 INTERCEPTOR[object Object]
VM1604 app.bundle.js:1527 start login
VM1604 app.bundle.js:89392 BEGIN Authorize Code Flow, no auth data
VM1604 app.bundle.js:89392 AuthorizedController created. local state: 15821982471040.47561711387714610.5390697281765799
log.js:24 [HMR] Waiting for update signal from WDS...
log.js:24 [HMR] Waiting for update signal from WDS...
Navigated to https://localhost.myDomain:8083/callback?code=b30915bfb9a0f8ea838b685b467fd33c02c3e3282210e9ca11807665a8116d87&scope=cc.api&state=15821982471040.47561711387714610.5390697281765799
log.js:24 [HMR] Waiting for update signal from WDS...
angular-auth-oidc-client.js:392 STS server: https://mySecurityMachine.myDomain/authentication
angular-auth-oidc-client.js:392 Silent Renew is active, check if token in storage is active
angular-auth-oidc-client.js:392 <iframe id=​"myiFrameForSilentRenew" style=​"display:​ none;​">​…​</iframe>​
auth.service.ts:105 Location {href: "https://localhost.myDomain:8083/callback?code=b3…15821982471040.47561711387714610.5390697281765799", ancestorOrigins: DOMStringList, origin: "https://localhost.myDomain:8083", protocol: "https:", host: "localhost.myDomain:8083", …}

Как показано в

VM1604 app.bundle.js:89392 {access_token: "eyJhbGciOiJSUzI1NiIsImtpZCI6IkJFODdGRjgwRjhBMkQyMT…V9-uKxgRq46JSiVpZbUSm_d7GB07aO1k4SvRWmIqw20Fo7ADA", expires_in: 3600, token_type: "Bearer", state: "15821982471040.47561711387714610.5390697281765799", session_state: "45871.95561094084"}

Что мне не хватает, чтобы перенаправить на нужную страницу?

Ответы [ 2 ]

0 голосов
/ 27 февраля 2020

То, что я в итоге сделал, - это сохранение URL-адреса запроса в локальном хранилище в TokenInterceptorService с

private currentUrlIndex = 'UrlBeforeLoginRedirect';
console.log("After the login will redirect to " + window.location.href);
if(this.getFromLocalStorage(this.currentUrlIndex) == null)
    this.setOnLocalStorage(this.currentUrlIndex, window.location.href);

this.authService.login();
let tokenFromNewAuthentication = this.authService.getToken();
resultingToken = tokenFromNewAuthentication;

. Я настроил redirectUri для перенаправления меня в конечную точку myApp / callback, где класс component.ts проверяет, установлено ли значение URL в локальном хранилище. Если есть, то он перенаправляет на него

if (this.authService.isAuthorized) {
var redirectWhereTheUserWasBeforeLoginRedirect = this.getFromLocalStorage(this.currentUrlIndex);
console.error("Login done: will redirect to " + redirectWhereTheUserWasBeforeLoginRedirect);
    if (redirectWhereTheUserWasBeforeLoginRedirect != null) {
        console.log("Removing the entry " + this.currentUrlIndex + " from the local storage");
        this.removeFromLocalStorage(this.currentUrlIndex);
        debugger;
        window.location.href = redirectWhereTheUserWasBeforeLoginRedirect;
    }
    else {
        console.log("Could not redirect the user to the screen where she was before the login prompt.");
    }
}

Мне нравится это решение, поскольку оно перенаправляет на URL-адрес (читай специфика c запись), который был первоначально запрошен, а не на домашнюю страницу, как запеченный в растворе будет делать.

0 голосов
/ 27 февраля 2020

попробуйте этот популярный клиент angular -oauth2-oid c есть несколько инструкций, и все начинает работать. также этот пакет содержит отличный сервис авторизации, и вам больше не понадобится ваш сервис авторизации

...