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

Posted

技术标签:

【中文标题】如何从回调将 JWT 发送到 React 客户端?【英文标题】:How to send JWT to React client from a callback? 【发布时间】:2018-05-15 21:29:12 【问题描述】:

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

我愚蠢地卡住了一点,也许我没有理解一些东西。

让我们将我的项目命名为 MyApp:

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

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

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

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

我完全迷路了:(

谢谢!

【问题讨论】:

【参考方案1】:

您可以将 JWT 放入您要将用户重定向到的 URL 的查询字符串中。然后,您通过访问查询字符串获取客户端中的 JWT,并将其保存到本地存储。我最近自己做了,看看我的回购server here,client 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>
            );
        
    

【讨论】:

请在此处添加示例代码,这将大大改善答案。 @Shudrum 没问题,如果我的回答能帮助您随时接受并支持它:D 重复使用JWT时上述方法安全吗? 这种方法可能不安全。见:***.com/questions/32722952/…【参考方案2】:

这就是我在我的应用程序中执行此操作的方式,请注意,由于 Thomas 提到的与 here 相关联的安全问题,这与 Dimitris 的回答相比略有改进,并且需要 Redis(或数据库如果你喜欢的话)。

我没有直接在查询参数上传递 JWT,而是:

    生成一个随机令牌(在我的例子中是 64 个字母数字字符) 以随机令牌作为键存储到您的 Redis,jwt 作为 TTL 短的值(我设置为 1 分钟)
redisClient.set(randomToken, jwtToken, "EX", 60);
    将令牌传递到重定向 URL 上的查询参数中
res.redirect(`http://example.com/jwt?token=$randomToken`);
    在您的客户端上,从查询参数中获取令牌,并向您的快速路由发送 HTTP 请求,以使用令牌从 redis 获取 JWT
router.get("/getJwt/:token", (req, res, next) => 
    const jwt = redisClient.get(token);
    // delete the JWT from redis
    redisClient.del(token);

    // you can return 401 if JWT doesnt exists on the Redis server
    res.json(jwt);
);
    将 jwt 存储在客户端的本地存储中

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

【讨论】:

以上是关于如何从回调将 JWT 发送到 React 客户端?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 expressjs 发送 jwt 令牌以响应应用程序? [关闭]

如何将我的 Instagram 访问令牌从我的 Express 服务器发送到 React 客户端?

spring boot - 假装客户端发送基本授权标头|将 jwt 令牌从一个微服务传递到另一个微服务

如何在服务器发送的reactjs中获取JWT cookie

如何将 JWT 令牌从 PHP 发送到 Angular?

如何发送和接收 JWT 令牌?