【问题标题】:Using dropboxjs to authenticate the client with oauth 2. What about the server?使用 dropboxjs 通过 oauth 对客户端进行身份验证 2. 服务器呢?
【发布时间】:2013-08-01 23:18:56
【问题描述】:

我是 Oauth 和服务器端方面的新手,所以请耐心等待。

我有一个使用 dropbox-js 对用户进行身份验证的 Web 应用程序。一切都很简单。该应用程序使用 dropbox-js 的 client.authenticate 函数,如果用户通过身份验证,应用程序将自动重定向到初始页面,并在该页面执行身份验证回调。从那一刻起,我知道我很高兴通过 Dropbox 进行了身份验证,并且可以使用应用程序的 Dropbox 目录进行操作。

我有一个当前什么都不做的公共 node.js 服务器。我想做的是:

  • 客户端通过身份验证后,立即调用我的服务器并告诉它用户已通过身份验证
  • 如果用户在服务器数据库中不存在,则为他/她在用户数据库中创建一个条目(我不需要详细说明来执行此操作)。如果存在,则发回用户的关联数据。

我怎样才能以安全的方式做到这一点?我的意思是,服务器如何判断用户是有效的 Dropbox 用户?服务器是否应该使用用户凭据对 Dropbox 进行身份验证?这些情况下的工作流程是什么?

【问题讨论】:

    标签: node.js oauth oauth-2.0 dropbox dropbox-api


    【解决方案1】:

    在身份验证过程结束时,您将获得一个访问令牌,用于调用 API。如果客户端和服务器都需要调用 API,那么两者都需要有访问令牌。

    如果您今天在客户端进行身份验证,您可以以某种方式提取访问令牌(不确定它是否/如何从库中公开,但它在某处并存储在本地存储中)并传递它到服务器。然后,服务器可以使用它调用/account/info 并获取经过身份验证的用户的 Dropbox 用户 ID。

    另一种方法是反其道而行之。使用“代码流”(而不是“令牌流”)对用户进行身份验证,并首先在服务器上获取访问令牌。然后您可以将其传递给客户端,并将其作为Dropbox.Client constructor 中的选项传递。我认为dropbox-js 本身就支持这一点,但自己做也不难。下面是一些用于登录用户并显示其姓名的原始 Express 代码:

    var crypto = require('crypto'),
        express = require('express'),
        request = require('request'),
        url = require('url');
    
    var app = express();
    app.use(express.cookieParser());
    
    // insert your app key and secret here
    var appkey = '<your app key>';
    var appsecret = '<your app secret>';
    
    function generateCSRFToken() {
        return crypto.randomBytes(18).toString('base64')
            .replace(/\//g, '-').replace(/\+/g, '_');
    }
    function generateRedirectURI(req) {
        return url.format({
                protocol: req.protocol,
                host: req.headers.host,
                pathname: app.path() + '/callback'
        });
    }
    
    app.get('/', function (req, res) {
        var csrfToken = generateCSRFToken();
        res.cookie('csrf', csrfToken);
        res.redirect(url.format({
            protocol: 'https',
            hostname: 'www.dropbox.com',
            pathname: '1/oauth2/authorize',
            query: {
                client_id: appkey,
                response_type: 'code',
                state: csrfToken,
                redirect_uri: generateRedirectURI(req)
            }
        }));
    });
    
    app.get('/callback', function (req, res) {
        if (req.query.error) {
            return res.send('ERROR ' + req.query.error + ': ' + req.query.error_description);
        }
    
        // check CSRF token
        if (req.query.state !== req.cookies.csrf) {
            return res.status(401).send(
                'CSRF token mismatch, possible cross-site request forgery attempt.'
            );
        } else {
            // exchange access code for bearer token
            request.post('https://api.dropbox.com/1/oauth2/token', {
                form: {
                    code: req.query.code,
                    grant_type: 'authorization_code',
                    redirect_uri: generateRedirectURI(req)
                },
                auth: {
                    user: appkey,
                    pass: appsecret
                }
            }, function (error, response, body) {
                var data = JSON.parse(body);
    
                if (data.error) {
                    return res.send('ERROR: ' + data.error);
                }
    
                // extract bearer token
                var token = data.access_token;
    
                // use the bearer token to make API calls
                request.get('https://api.dropbox.com/1/account/info', {
                    headers: { Authorization: 'Bearer ' + token }
                }, function (error, response, body) {
                    res.send('Logged in successfully as ' + JSON.parse(body).display_name + '.');
                });
    
                // write a file
                // request.put('https://api-content.dropbox.com/1/files_put/auto/hello.txt', {
                //  body: 'Hello, World!',
                //  headers: { Authorization: 'Bearer ' + token }
                // });
            });
        }
    });
    
    app.listen(8000);
    

    【讨论】:

    • 我选择第一个选项(我已经准备好了一切)。请注意,服务器不需要使用 Dropbox API,我只想进行身份验证(也就是我想在服务器上知道我有一个具有有效凭据的有效用户)。所以工作流程 1) 是将令牌发送到服务器 -> 2) 在服务器上获取令牌对应的用户名 -> 3) 更新与用户名对应的表条目?如果是,此工作流程是否存在安全漏洞?
    • 您应该使用用户 ID (uid) 而不是名称,因为名称可能会发生冲突。你也可以从/account/info 那里得到它。
    • 关于安全性,您必须将访问令牌发送到服务器,并在那里进行 /account/info 调用。您不能从客户端传递 uid,并让服务器信任它。你可能已经知道了,我添加它只是为了确保。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-23
    • 2017-04-29
    • 2017-10-07
    • 2012-08-28
    相关资源
    最近更新 更多