【问题标题】:Get hash url parameters to redirect to requesting url when using Auth0 with Angular 2使用 Auth0 和 Angular 2 时获取哈希 url 参数以重定向到请求 url
【发布时间】:2017-04-14 02:01:53
【问题描述】:

Auth0 要求您在身份验证后将回调 URL 列入白名单,因此您不能只使用 /thing/1、/thing/1001 等 URL 登录应用程序中的任何页面,因为无法通配事物 ID。

这个Github conversation 指向我在Angular 2 中解释为的简洁解决方案:

在我的 auth.service.ts 中:

lock = new Auth0Lock('Client_ID', 'Domain', {
        auth: {
            redirectUrl: window.location.origin + '/login',
            responseType: 'token',
            params: {
                scope: 'openid name email',
                state: JSON.stringify({pathname: window.location.pathname})
            }
        }
    });

然后我可以在 Angular 2 中将我的 /login 路由列入白名单。我尝试让我的 login.component.ts 读取,然后导航到 Auth0 在回调中返回的路径名,如下所示:

this.route
    .queryParams
    .subscribe(params => {
         this.state = params['state'];
         this.router.navigate(this.state, {preserveQueryParams: true});
     });

...但是 params 似乎总是为空。据我所知,这是因为回调 URL 采用这种形式:

http://localhost:5555/login#access_token=...&id_token=...&token_type=Bearer&state=%7B%22pathname%22%3A%22%2Flogin%22%7D

...并且 Angular 2 路由会在 # 参数到达 LoginComponent 之前自动剥离它们。

然后我发现 lock.authenticated 上的 authResult 仍然包含我在 auth.service.ts 中的状态参数,因此尝试按如下方式导航到它:

this.lock.on("authenticated", (authResult:any) => {
    localStorage.setItem('id_token', authResult.idToken);
    let state: string = JSON.parse(authResult.state);
    this.router.navigate([state.pathname], {});

这似乎最初有效,但事实证明,并不可靠......它似乎将我从任何地方重定向到不可预测的任何地方

/thing/1001 to 
/things or even 
/

...我不知道为什么。非常感谢任何帮助。

编辑回应@shusson 的回答:

基于@shusson 答案的工作代码在 auth.service.ts 中:

export class Auth {
    // Configure Auth0
    lock = new Auth0Lock('Client_ID', 'Domain',{
        auth: {
            redirectUrl: location.origin + '/login',
            responseType: 'token',
        }
    });

    constructor(private router: Router, route: ActivatedRoute) {

        // Add callback for lock `authenticated` event
        this.lock.on("authenticated", (authResult:any) => {
            localStorage.setItem('id_token', authResult.idToken);
            let state: any = JSON.parse(authResult.state);
            this.router.navigate([state.pathname], {});
        ...
     }

    public login() {
        // Call the show method to display the widget.
        this.lock.show({
            auth: {
                params: {
                    scope: 'openid name email',
                    state: JSON.stringify({pathname: this.router.url})
                }
            }
        });
    };

编辑:基于this comment re:在回调中传递路径是一个 CSRF 漏洞:

我的 auth.service.ts 中的最终工作代码是:

import { Injectable }      from '@angular/core';
import { tokenNotExpired } from 'angular2-jwt/angular2-jwt';
import { Router, ActivatedRoute } from '@angular/router';
import { UUID } from 'angular2-uuid/index';

// Avoid name not found warnings
declare var Auth0Lock: any;

@Injectable()
export class Auth {
    // Configure Auth0
    lock = new Auth0Lock('Client_ID', 'Domain',{
        auth: {
            redirectUrl: location.origin + '/login',
            responseType: 'token',
        }
    });
    //Store profile object in auth class
    userProfile: Object;

    constructor(private router: Router, route: ActivatedRoute) {

        // Set userProfile attribute of already saved profile
        this.userProfile = JSON.parse(localStorage.getItem('profile'));

        // Add callback for lock `authenticated` event
        this.lock.on("authenticated", (authResult:any) => {
            localStorage.setItem('id_token', authResult.idToken);
            let pathname_object: any = JSON.parse(authResult.state);
            let pathname: any = localStorage.getItem(pathname_object.pathname_key);
            //get rid of localStorage of url
            localStorage.removeItem(pathname_object.pathname_key);
            //navigate to original url
            this.router.navigate([pathname], {});

        // Fetch profile information
        this.lock.getProfile(authResult.idToken, (error:any, profile:any) => {
            if (error) {
                // Handle error
                alert(error);
                return;
            }

            localStorage.setItem('profile', JSON.stringify(profile));
                this.userProfile = profile;
            });
        });
    }

    public login() {
        //generate UUID against which to store path
        let uuid = UUID.UUID();
        localStorage.setItem(uuid, this.router.url);
        // Call the show method to display the widget.
        this.lock.show({
            auth: {
                params: {
                    scope: 'openid name email',
                    state: JSON.stringify({pathname_key: uuid})
                }
            }
        });
    };
...
}

【问题讨论】:

    标签: angular auth0


    【解决方案1】:

    在我们的身份验证服务中,我们执行以下操作:

    const options: any = {
        auth: {
            redirectUrl: location.origin,
            responseType: 'token'
        },
    };
    
    constructor(private router: Router) {
        new Auth0Lock(environment.auth0ClientId, environment.auth0Domain, options);
        ...
        this.lock.on('authenticated', (authResult: any) => {
            ...
            this.router.navigateByUrl(authResult.state);
        });
    }
    
    public login() {
        this.lock.show({
            auth: {
                params: {state: this.router.url},
            }
        });
    };
    

    【讨论】:

    • 谢谢@shusson。我必须做的唯一改变是我必须在 Auth0Lock 的实例化中设置 redirectUrl,否则 Auth0 似乎会忽略它。我会在上面编辑。我在 Auth0 论坛上也有回复:auth0.com/forum/t/… 建议最好将返回路径存储在 localStorage 中,针对随机 UUIDv4 密钥,以状态发送,然后使用它来调用路径在 .on('authenticated') 中作为“将整个路由信息存储在 state 中会导致 CSRF 漏洞”
    • 很高兴听到。我已经修复了您提到的错误,实际上我在我们的服务中也做了同样的事情(我认为对于示例,我会尝试更简洁:p)。你知道在 state 中存储整个路由信息是如何导致 CSRF 漏洞的吗?
    • 没问题 - 它让我走上了正轨。我在 Auth0 论坛上问过 - auth0.com/forum/t/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-14
    • 1970-01-01
    • 2011-12-11
    • 1970-01-01
    • 1970-01-01
    • 2019-10-28
    • 2018-09-14
    相关资源
    最近更新 更多