使用 Auth0Lock 与 express + angular2 应用程序进行身份验证,“UnauthorizedError: jwt malformed”

Posted

技术标签:

【中文标题】使用 Auth0Lock 与 express + angular2 应用程序进行身份验证,“UnauthorizedError: jwt malformed”【英文标题】:Using Auth0Lock for authentication with express + angular2 app, "UnauthorizedError: jwt malformed" 【发布时间】:2017-11-11 06:08:57 【问题描述】:

TL;DR:登录后 JWT 保存在客户端(来自 auth0lock),当使用 angular2-jwt 发送到服务器端时,使用 express-jwt 验证时接收:“UnauthorizedError: jwt malformed”

你好,我在做SPA,前端是angular2,后端是express, 我正在研究的当前功能是身份验证,经过一些研究后,我认为最好的解决方案是使用 Auth0。

我正在使用 Auth0Lock,因为我不喜欢托管页面,并且退出 SPA,我遇到的问题是登录后,我创建了一个按钮来测试身份验证是否通过我的服务器端,然后单击它,我在服务器端收到“UnauthorizedError: jwt malformed”。

我的登录流程一开始只是客户端,你点击登录按钮,你得到Auth0Lock,你登录,一个JWT保存在客户端,一旦你登录,当你点击测试按钮,使用Angular2-jwt模块将JWT传递到服务器端,并在服务器端验证JWT。

客户端认证服务:

@Injectable()
export class AuthService 

  private lock: any;
  private userProfile: any;

  constructor(public router: Router) 
    this.lock = new Auth0Lock(AUTH_CONFIG.clientID, AUTH_CONFIG.domain, 
    oidcConformant: true,
    autoclose: true,
    auth: 
      redirectUrl: AUTH_CONFIG.callbackURL,
      responseType: 'token id_token',
      audience: AUTH_CONFIG.audience,
      params: 
        scope: 'openid profile user_metadata email'
      
    ,
    rememberLastLogin: true,
    allowForgotPassword: true,
    languageDictionary:  title: 'Cowoffee',
    socialButtonStyle: 'small'
  );
  

  public login(): void 
    this.lock.show();
  

  // Call this method in app.component
  // if using path-based routing
  public handleAuthentication(): void 
    this.lock.on('authenticated', (authResult) => 
      if (authResult && authResult.accessToken && authResult.idToken) 
        this.setSession(authResult);
        this.router.navigate(['/']);
      
    );
    this.lock.on('authorization_error', (err) => 
      this.router.navigate(['/']);
      console.log(err);
      alert(`Error: $err.error. Check the console for further details.`);
    );
  

  // Call this method in app.component
  // if using hash-based routing
  public handleAuthenticationWithHash(): void 
    this
      .router
      .events
      .filter(event => event instanceof NavigationStart)
      .filter((event: NavigationStart) => (/access_token|id_token|error/).test(event.url))
      .subscribe(() => 
        this.lock.resumeAuth(window.location.hash, (err, authResult) => 
          if (err) 
            this.router.navigate(['/']);
            console.log(err);
            alert(`Error: $err.error. Check the console for further details.`);
            return;
          
          this.setSession(authResult);
          this.router.navigate(['/']);
        );
    );
  

  private setSession(authResult): void 
    // Set the time that the access token will expire at
    const expiresAt = JSON.stringify((authResult.expiresIn * 1000) + new Date().getTime());
    localStorage.setItem('access_token', authResult.accessToken);
    localStorage.setItem('id_token', authResult.idToken);
    localStorage.setItem('expires_at', expiresAt);
  

  public logout(): void 
    // Remove tokens and expiry time from localStorage
    localStorage.removeItem('access_token');
    localStorage.removeItem('id_token');
    localStorage.removeItem('expires_at');
    // Go back to the home route
    this.router.navigate(['/']);
  

  public isAuthenticated(): boolean 
    // Check whether the current time is past the
    // access token's expiry time
    const expiresAt = JSON.parse(localStorage.getItem('expires_at'));
    return new Date().getTime() < expiresAt;
  

  public async getProfile(): Promise<any> 
    if (this.userProfile) 
      return this.userProfile;
    

    const accessToken = localStorage.getItem('access_token');
    if (!accessToken) 
      throw new Error('Access token must exist to fetch profile');
    
    const test: any = promisifyAll(this.lock);
    this.userProfile = test.getUserInfoAsync(accessToken);
    return this.userProfile;
  


客户端提供者:

export function authHttpServiceFactory(http: Http, options: RequestOptions) 
  return new AuthHttp(new AuthConfig(
    tokenGetter: (() => sessionStorage.getItem('access_token')),
    globalHeaders: ['Content-Type': 'application/json'],
  ), http, options);

服务器端验证设置:

export const JwtConfiguration = 
  // Dynamically provide a signing key
  // based on the kid in the header and
  // the singing keys provided by the JWKS endpoint.
  secret: jwksRsa.expressJwtSecret(
    cache: true,
    rateLimit: true,
    jwksRequestsPerMinute: 5,
    jwksUri: `https://*******.eu.auth0.com/.well-known/jwks.json`
  ),

  // Validate the audience and the issuer.
  audience: 'https://*******',
  issuer: `https://*******.eu.auth0.com/`,
  algorithms: ['RS256']
;

控制器内的服务端验证:

@JsonController('/test')
export class TestController 
    @Get('/')
    @UndefinedResultCode(500)
    @UseBefore(jwt(JwtConfiguration))
    public TestJWT(): string 
        return 'success';
        

我想说大部分代码是从 Auth0 文档中复制和使用的,非常感谢您的帮助,很抱歉这么长的帖子!

更新: 问题是令牌保存在 localStorage 中,而我试图从 sessionStorage 中检索它,相当尴尬,当你只是复制粘贴示例时会发生......之后一切正常。

【问题讨论】:

问题是令牌保存在 localStorage 中,而我试图从 sessionStorage 中检索它,当您只是复制粘贴示例时会发生相当尴尬的情况......之后一切正常。 【参考方案1】:

问题是令牌保存在 localStorage 中,而我试图从 sessionStorage 中检索它,当您只是复制粘贴示例时会发生相当尴尬的情况......之后一切正常。

如果有人在使用Auth0,请注意new community,旧的没用,祝大家好运。 :)

【讨论】:

以上是关于使用 Auth0Lock 与 express + angular2 应用程序进行身份验证,“UnauthorizedError: jwt malformed”的主要内容,如果未能解决你的问题,请参考以下文章

Auth0 Lock 中不触发鉴权回调函数

如何使用 Auth0 Lock 从响应对象中获取用户名?

express框架的安装与使用

如何将 webpack 与 express 一起使用?

Ajax--express框架介绍与基本使用

express与express-art-template两者相结合使用方法