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

NbOAuth2AuthStrategy show errors from login response #6010

Open
1 of 3 tasks
GiamBoscaro opened this issue Nov 27, 2023 · 1 comment
Open
1 of 3 tasks

NbOAuth2AuthStrategy show errors from login response #6010

GiamBoscaro opened this issue Nov 27, 2023 · 1 comment

Comments

@GiamBoscaro
Copy link

Issue type

  • bug report
  • feature request
  • question about the decisions made in the repository

Issue description

I have setup a login through Keycloak. When I login with wrong credentials, I do see Keycloak responding with "Invalid credentials". I would like to display this error, but "Something went wrong" is always shown. How can I show the error coming from Keycloak? I have already checked similar issues like #442 and #1835 but there is no "errors" property in the NbOAuth2AuthStrategy setup.

Current behavior:
When I login with invalid credentials, the error "Something went wrong" is shown.

Expected behavior:
The error in the login response should be shown

Steps to reproduce:
Login with invalid credentials using the NbOAuth2AuthStrategy

Related code:

NbOAuth2AuthStrategy.setup({
        name: 'keycloak',
        baseEndpoint: environment.keycloak.uri,
        clientId: environment.keycloak.client_id,
        clientSecret: environment.keycloak.client_secret,
        clientAuthMethod: NbOAuth2ClientAuthMethod.BASIC,
        authorize: {
          endpoint: '/auth',
          responseType: NbOAuth2ResponseType.TOKEN,
        },
        token: {
          endpoint: '/token',
          grantType: NbOAuth2GrantType.PASSWORD,
          class: NbAuthOAuth2JWTToken,
        },
        refresh: {
          endpoint: '/token',
          grantType: NbOAuth2GrantType.REFRESH_TOKEN,
        },
      })

Other information:

node v14.21.3

Angular, Nebular

Angular 13.3.12
Nebular 9.1.0-rc.8
@GiamBoscaro
Copy link
Author

up the issue since I never got an answer in 9 months. I have solved the issue by extending the NbOAuth2AuthStrategy. I don't totally like this solution so if you have something better let me know.
The problem here is that the method passwordToken is using Angular HttpClient to authenticate, and this client returns just a string as an error. Even by forcing responseType: 'json', the HttpClient still returns a string instead of a HttpErrorResponse.
What I have done is extending NbOAuth2AuthStrategy and overwriting the passwordToken and handleResponseError methods to be able to use Axios.

import { Injectable } from '@angular/core';
import { HttpErrorResponse, HttpHeaders } from '@angular/common/http';
import { Observable, from, of as observableOf } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
import { NbAuthIllegalTokenError, NbAuthResult, NbAuthStrategyClass, NbOAuth2AuthStrategy, NbOAuth2AuthStrategyOptions } from '@nebular/auth';
import axios, { AxiosHeaders, AxiosError } from "axios";

@Injectable()
export class KeycloakAuthStrategy extends NbOAuth2AuthStrategy {
    static setup(options: NbOAuth2AuthStrategyOptions): [NbAuthStrategyClass, NbOAuth2AuthStrategyOptions] {
      return [KeycloakAuthStrategy, options];
    }

    private httpHeadersToAxiosHeaders(header: HttpHeaders): AxiosHeaders {      
      const axiosHeaders = new AxiosHeaders({});
      header.keys().forEach(key => {
        axiosHeaders.set(key, header.get(key))     
      });
      return axiosHeaders;
    }

    passwordToken(username: string, password: string): Observable<NbAuthResult> {
      const module = 'token';
      const url = this.getActionEndpoint(module);
      const requireValidToken = this.getOption(`${module}.requireValidToken`);

      const axiosPromise = axios.post(url,  this.buildPasswordRequestData(username, password), { headers: this.httpHeadersToAxiosHeaders(this.getHeaders()) });
      return from(axiosPromise).pipe(
        map((res) => {
          res = res.data;
          return new NbAuthResult(
            true,
            res,
            this.getOption('redirect.success'),
            [],
            this.getOption('defaultMessages'),
            this.createToken(res, requireValidToken),
          );
        }),
        catchError((res) => this.handleResponseError(res)),
      );
    }

    protected handleResponseError(res: any): Observable<NbAuthResult> {
      let errors = [];
      if (res instanceof HttpErrorResponse) {
        if (res.error.error_description) {
          errors.push(res.error.error_description);
        } else {
          errors = this.getOption('defaultErrors');
        }
      } else if (res instanceof NbAuthIllegalTokenError) {
        errors.push(res.message);
      } else if (res instanceof AxiosError) {
        errors.push(res.response.data["error_description"]);
      } else {
        errors.push('Something went wrong.');
      }
  
      return observableOf(new NbAuthResult(false, res, this.getOption('redirect.failure'), errors, []));
    }

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant