简体   繁体   English

OAuth2 Discord授权弹窗流程

[英]OAuth2 Discord authorization popup flow

I'm working on an admin panel for my discord bot and I'm currently implementing OAuth2 authorization.我正在为我的 discord 机器人开发一个管理面板,我目前正在实施 OAuth2 授权。

My frontend and backend are hosted on two separate servers, and I don't exactly know how to handle sending/storing the access token.我的前端和后端托管在两个独立的服务器上,我不知道如何处理发送/存储访问令牌。 My current workflow relies on redirecting the discord authorization to the backend (I don't want it to redirect to frontend, because it would involve additional calls to the backend with the client code to create the token anyway), and I'm able to call the discord API with received client code and get the access token, but I have no idea how to store it properly.我当前的工作流程依赖于将 discord 授权重定向到后端(我不希望它重定向到前端,因为它会涉及使用客户端代码对后端进行额外调用以创建令牌),并且我能够使用收到的客户端代码调用 discord API 并获取访问令牌,但我不知道如何正确存储它。

Current flow:当前流量:

  1. In the browser (localhost:3000) I click the Login button that opens the popup with discord authorization site.在浏览器 (localhost:3000) 中,我单击“登录”按钮,打开带有 discord 授权站点的弹出窗口。
const LINK = "https://discord.com/api/oauth2/authorize?client_id=somecode&redirect_uri=http%3A%2F%2F127.0.0.1%3A3010%2Fapi%2Fauth&response_type=code&scope=identify%20guilds";

function onLinkClicked(event: Event) {
    event.preventDefault();

    const popup = window.open(LINK, 'popup', 'width=600,height=800');
}
  1. After authorization I redirect to the server url (localhost:3010/api/auth?code=somecode).授权后我重定向到服务器 url (localhost:3010/api/auth?code=somecode)。
  2. In there I'm making a call to the discord API to receive access token.我在那里打电话给 discord API 以接收访问令牌。
MainRouter.get('/auth', async (req, res) => {
    const code = req.query.code;
    const params: any = {
        'client_id': process.env.APP_ID,
        'client_secret': process.env.APP_SECRET,
        'grant_type': 'authorization_code',
        'code': code,
        'redirect_uri': 'http://127.0.0.1:3010/api/auth'
    };

    try {
        const tokenResponse = await axios.post(`${API_ENDPOINT}/oauth2/token`, new URLSearchParams(params));
        req.session.token = tokenResponse.data.access_token; // when I make another call from the frontend, it doesn't see the token stored in the session here, because I'm saving it in the wrong place, sessionID here doesn't match my sessionID when I make calls from the frontend itself.
        res.send('OK');
    } catch(error) {
        console.error(error);
    }
});

And now I'm stuck because I can't store the token in the session/cookies and I also don't know how could I store it in the database.现在我被卡住了,因为我无法将令牌存储在会话/cookie 中,而且我也不知道如何将其存储在数据库中。

I don't want the discord authorization to redirect to the frontend site, where I will make another call to the backend with the client code and then convert it into access token and store it, because it seems messy and I hope there could be another solution.我不希望 discord 授权重定向到前端站点,在那里我将使用客户端代码再次调用后端,然后将其转换为访问令牌并存储它,因为它看起来很乱,我希望可以有另一个解决方案。

I saw that probot.io site, when it creates authorization popup, it then redirects you to the api.probot.io?code=somecode, which is a different site, closes immediately and logs you into the dashboard and I don't know how to achieve similar effect.我看到 probot.io 网站,当它创建授权弹出窗口时,它会将您重定向到 api.probot.io?code=somecode,这是一个不同的网站,立即关闭并让您登录到仪表板,我不知道如何达到类似的效果。

So, my question is, is there a way to store the access token in the frontend session, when I'm not calling from the frontend?所以,我的问题是,当我不从前端调用时,有没有办法将访问令牌存储在前端 session 中? And since the answer is probably no, how can improve this workflow?既然答案可能是否定的,那么如何改进这个工作流程呢?

I came up with solution that seems okay, but I'm opened to suggestions.我想出了一个看起来不错的解决方案,但我愿意接受建议。

Instead of instantly getting access token after the redirect to the /api/auth on backend, I just send the message from the popup to the main client containing the client code.在重定向到后端的 /api/auth 后,我没有立即获取访问令牌,而是将消息从弹出窗口发送到包含客户端代码的主客户端。

MainRouter.get('/auth', async (req, res) => {
    const code = req.query.code;
    
    res.set('Content-Type', 'text/html');
    res.send(Buffer.from(`<script>window.opener.postMessage("${code}", "http://localhost:3000");window.close();</script>`));
});

After receiving the code on client I make another /api/auth call, but this time it is POST and it expects the code as a parameter.在客户端收到代码后,我又进行了一次 /api/auth 调用,但这次是 POST,它希望将代码作为参数。

window.addEventListener('message', async (event) => {
    if(event.origin == 'http://127.0.0.1:3010') {
        console.log(event.data);
        const res = await callAPI('/auth', { code: event.data }, 'post');
        console.log(res);
    }
});

The backend then will exchange it for the access token and store it in the session, which this time works perfectly.然后后端会将其交换为访问令牌并将其存储在 session 中,这次可以完美运行。

MainRouter.post('/auth', async (req, res) => {
    const code = req.body.code;
    const params: any = {
        'client_id': process.env.APP_ID,
        'client_secret': process.env.APP_SECRET,
        'grant_type': 'authorization_code',
        'code': code,
        'redirect_uri': 'http://127.0.0.1:3010/api/auth'
    };

    try {
        const tokenResponse = await axios.post(`${API_ENDPOINT}/oauth2/token`, new URLSearchParams(params));
        console.log(tokenResponse.data.access_token);

        req.session.token = tokenResponse.data.access_token; // this will work now, since I'm calling from the frontend side
        res.send({ code: 200 });
    } catch(error) {
        res.send({ code: 301, msg: error });
    }
});

I came up with this after realizing that I can send the messages from the popup to the main, but I don't know if it's the most optimal and somewhat secure solution.在意识到我可以将消息从弹出窗口发送到主窗口后,我想到了这个,但我不知道它是否是最佳和安全的解决方案。 I'm thinking if there exists a "cleaner" solution to the problem though...我在想是否存在解决问题的“更清洁”的解决方案......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM