如何从回调中将JWT发送到React客户端?

时间:2017-12-01 18:06:19

标签: reactjs oauth jwt

我正在尝试创建一个无密码身份验证的应用程序,以及Facebook和邮件等社交媒体。

我愚蠢地坚持了一点,也许我不明白。

让我的项目命名为MyApp:

如果用户尝试从myapp.com/登录Facebook,则会将其重定向到facebook.com/login,然后Facebook将其重定向到myapp.com/callback

因此,在我的服务器上,路由//callback完全相同:它们只是将我的React应用程序发送给用户,但/callback生成JWT令牌。

从这里开始我的问题是:如何在React应用程序的同时将JWT令牌发送到客户端?

也许我错过了其他提供商的东西,但邮件的无密码原则是一回事:只是一个验证用户的链接,只是重定向到/callback,没有外部提供商。

我完全迷失了:(

谢谢!

2 个答案:

答案 0 :(得分:3)

您可以将JWT放入要将用户重定向到的URL的查询字符串中。然后,您从客户端获取JWT以访问查询字符串,并将其保存到本地存储。我最近自己完成了检查我的回购server hereclient here

编辑:我在服务器中发布回调路由的代码,并在客户端获取令牌以获取更多详细信息,检查上面的回购链接

回拨路线

app.get('/auth/github/callback', (req,res) => {
githubAuth.code.getToken(req.originalUrl)
    .then(user => {
        console.log(user.data);
        return fetch('https://api.github.com/user?access_token=' + user.accessToken);
    })
    .then(result => {
        return result.json();
    })
    .then(json => {
        //console.log(json);
        //res.send(json);
        return User.findOne({githubId: json.id})
            .then(currentUser => {
                if(currentUser){  // user already exists
                    console.log('User exists with GitHub Login: ' + currentUser);
                    return currentUser;
                }
                else{  // User doesn't exist
                // saved automatically using mongoose, returns Promise
                    return new User({
                        username: json.login,
                        githubId: json.id
                    }).save()
                        .then(newUser => {
                            console.log('New User created with GitHub Login: ' + newUser);
                            return newUser;
                        });
                }
            });
    })
    .then(user => {
        // Now use user data to create a jwt
        const token = jwt.sign({id: user._id}, process.env.JWT_ENCRYPTION_KEY, {
            expiresIn: 86400  // expires in 24 hours
        });

        const encodedToken = encodeURIComponent(token);
        res.status(200);
        res.redirect('/profile?token=' + encodedToken);
    })
    .catch(err => console.log(err));
});

客户端中的令牌检索

class UserProfile extends React.Component{
    constructor(props){
        super(props);
        this.state = {username: ''};
    }

    componentDidMount(){
        const query = new URLSearchParams(this.props.location.search)

        // When the URL is /the-path?some-key=a-value ...
        let token = query.get('token')
        console.log(token)

        if(token){  // it's the first time we receive the token
            // we save it
            console.log('Saving token');
            localStorage.setItem('userjwt', token);
        }
        else{
            // if qrstring is empty we load the token from storage
            token = localStorage.userjwt;
            console.log('Loading token: ' + token);
        }

        if(token){
            const headers = new Headers({
                'x-access-token': token
            });

            const reqOptions = {
                method: 'GET',
                headers: headers
            };

            fetch('/user', reqOptions)
                .then(res => res.json())
                .then(user => {
                    console.log(user);
                    console.log(user.username);
                    this.setState({username: user.username});
                })
        }
    }

    render(){
        if(this.state.username === ''){
            return(
                <div className="social-profile">
                    <h2>Login first</h2>
                </div>
            );
        }
        else{
            return(
                <div className="social-profile">
                    <h2>User: {this.state.username}</h2>
                </div>
            );
        }
    }
}

答案 1 :(得分:2)

这就是我在应用程序中的操作方式,请注意,由于Thomas提到的与安全here相关联的安全问题(,并且需要Redis (或数据库(如果您愿意)。

我所做的不是代替直接在查询参数上传递JWT,而是:

  1. 生成随机令牌(在我的情况下为64个字母数字字符)
  2. 将随机令牌作为密钥存储到Redis中,将jwt作为具有短TTL的值存储到Redis中(我将我设置为1分钟)
localStorage
  1. 将令牌传递到重定向URL上的查询参数中
redisClient.set(randomToken, jwtToken, "EX", 60);
  1. 在您的客户端上,从查询参数中获取令牌,然后将HTTP请求发送到您的快速路由,以使用令牌从redis获取JWT
res.redirect(`http://example.com/jwt?token=${randomToken}`);
  1. 将jwt存储在客户端的本地存储中

这需要更多的工作,资源和一个额外的HTTP请求来获取JWT,但是考虑到直接在查询参数中传递JWT的风险,我认为这是值得的。并不是说用户会每分钟都进行身份验证。