Fitbit API的OAuth2隐式授权码与授权码授予

时间:2019-08-27 14:08:30

标签: javascript node.js ajax oauth-2.0

该问题的答案实际上是我在客户端使用授权代码授予流。.这是导致阻塞错误的原因。

在下面检查功能验证OAUTH2隐式授予Fitbit API。

我正在针对Fitbit API执行OAuth2身份验证。全部使用授权码授予流程。因此,首先获取身份验证代码,将其重定向到我的应用程序,然后将其交换为访问令牌,并使用此令牌获取数据。

从“ post_request.html”页面上的主页开始,按“ fitbit”按钮,将用户重定向到Fitbit的授权端点。我正在使用Node.js构建本地服务器来托管应用程序,并能够进行重定向而没有任何问题。

在重定向后,我检索了授权代码并发出AJAX POST请求以将授权代码换成访问令牌。然后,我发出此POST请求,但出现以下错误。

{"errors":[{"errorType":"invalid_client","message":"Invalid authorization header format. The header was not recognized to be a valid header for any of known implementations or a client_id was not specified in case of a public client Received header = BasicdW5kZWZpbmVk. Visit https://dev.fitbit.com/docs/oauth2 for more information on the Fitbit Web API authorization process."}],"success":false}

我认为在对client_id和client_secret进行编码的网址中可能存在错误。或者在我设置标题。但是我看不到它。谁能帮我吗?

我的HTML文件如下。

<body>

    <button onclick="fitbitAuth()">Fitbit</button>

    <!-- action = route, method = method -->
    <form action="/" method="POST" id="form">
        <h3>Email Address:</h3>
        <input type="email">
        <br>
        <h3>Password:</h3>
        <input type="password">     
        <br>
        <br>
        <button type="submit">Send Request</button>   
    </form>

</body>
</html>

我的脚本如下,它包含3个功能。 -base64编码功能 -用于启动OAuth2进程的onclick函数 -以身份验证代码交换访问令牌的功能

// run this script upon landing back on the page with the authorization code 
            // specify and/ or calculate parameters 
            var url_terug = window.location.search;
            var auth_code = url_terug.substr(6);
            var granttype = "authorization_code";
            var redirect_uri = "http://localhost:3000/fitbit";
            var client_id = "xxxxx";
            var client_secret = "xxxxxxxxxxxx";
            var stringto_encode = client_id + ":" + client_secret;
            var encoded_string = "";
            console.log("auth code = " + auth_code);

            function baseEncode(stringto_encode){

                encoded_string = btoa(stringto_encode);
                console.log(encoded_string);

            }

            function getTokenFitBit(){

                baseEncode();

                // execute a POST request with the right parameters 
                var request = new XMLHttpRequest();
                request.open('POST', "https://api.fitbit.com/oauth2/token?client_id=" + client_id + "&grant_type=" + granttype + "&redirect_uri=" + redirect_uri + "&code=" + auth_code);

                request.setRequestHeader('Authorization', 'Basic'+ encoded_string);
                request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');

                // Setup our listener to process completed requests
                request.onload = function () {

                    // Process our return data
                    // status code between 200 and 300 indicates success
                    if (request.status >= 200 && request.status < 300) {

                        console.log('success!', request);
                        console.log(request.responseText);

                        // continue with API calls.. 


                    // you could set up a broader response handling if there is a use case for it
                    } else {

                        console.log('The current token request failed!');

                    }
                };

                request.send();                    

            }

            getTokenFitBit();




            // get the access token out of the JSON response 
            // execute a GET request on the API endpoint 
            // handle the data 

          // upon clicking fitbit button, starting off the oauth2 authentication 
          function fitbitAuth() {

                window.location.href = 'https://www.fitbit.com/oauth2/authorize?client_id=xxxxx&response_type=code&scope=activity&redirect_uri=http://localhost:3000/fitbit&prompt=consent';

            }

2 个答案:

答案 0 :(得分:1)

在上面的示例中,您似乎缺少“ Basic”标头与其在此行中的值之间的分隔符:

//check if there's a hardware on the device, then lunch the camera app on click
        if (hasCamera()) {
            //---
            snapFab.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    requestPermission();
                }
            });
            //---

        } else {
            snapFab.setEnabled(false);

            Toast.makeText(this, "You don't have a hardware to handle camera events", Toast.LENGTH_LONG).show();
        }

假设您正在构建基于Web的应用程序,那么您可能也想使用“隐式”流程进行研究:https://oauth.net/2/grant-types/implicit/

答案 1 :(得分:0)

以下代码起作用并使用隐式授予流。它与FitBit API,Node.js一起重定向到应用程序,然后再进行客户端身份验证。

Node.js本地服务器模块的代码

// PROJECT authenticating Fitbit OAuth2 Implicit Grant  
const express = require("express");
const app = express();
const filesys = require("fs");
const path = require("path");
// body parser module parses form data into server
const body_parse = require("body-parser");

// middleware to encrypt folder structure for users : can look into DevTools 
app.use('/public', express.static(path.join(__dirname, 'static')));
// allows us to parse url encoded forms 
app.use(body_parse.urlencoded({extended: false}));

// using readstream with chunks in buffer with security on the path 
app.get("/fitbit", (req, res) => {

    const readStream = filesys.createReadStream(path.join(__dirname,'static','post_request.html'));
    readStream.on('error', function(){

        /*handle error*/ 
        res.write("there is an error authenticating against fitbit api endpoint");

        });
    res.writeHead(200, {'Content-type' : 'text/html'});
    readStream.pipe(res);

});

// bodyparser parses data and adds to the body of the request 
app.get("/", (req, res, err) => {

    const readStream = filesys.createReadStream(path.join(__dirname,'static','start_page.html'));
    res.writeHead(200, {'Content-type' : 'text/html'});
    readStream.pipe(res);

});

app.listen(3000);

起始页的HTML文件,仅带有一个按钮即可启动OAuth2进程。

<body>

    <button onclick="fitbitAuth()">Fitbit</button>

<script>
        // define variables 
        var cli_id = "xxxxx";
        var res_typ = "token";
        var scope_typ = "activity";
        var redirect = "http://localhost:3000/fitbit";
        var expires = "31536000";
        var prompt_var = "consent";

        // upon clicking fitbit button, starting off the oauth2 authentication 
        function fitbitAuth() {

            window.location.href = "https://www.fitbit.com/oauth2/authorize?client_id=" + cli_id + "&response_type=" + res_typ + "&scope=" + scope_typ + "&redirect_uri=" + redirect + "&expires_in=" + expires + "&prompt=" + prompt_var;

        }

    </script>

</body>
</html>

重定向页面,在同意使用他/她的数据后,用户将被发送回该页面。仅需一个按钮即可启动有关生命周期活动统计信息的API调用。

<body>

    <!-- action = route, method = method -->
    <button type="submit" onclick="getActivityData()">Send Request</button>   


<script>

        // get out the accesstoken from the provided data 
        var url_terug = window.location.href;
        var split_after = "access_token=";
        var split_before = "&user_id";
        var after_string = url_terug.split(split_after).pop();
        var accesstoken = after_string.slice(0, after_string.indexOf(split_before));
        console.log(accesstoken);

        // getActivityData();

        function getActivityData(){

            // execute a POST request with the right parameters 
            var request = new XMLHttpRequest();
            request.open('GET', "https://api.fitbit.com/1/user/-/activities.json");

            request.setRequestHeader('Authorization', 'Bearer '+ accesstoken);

            // Setup our listener to process completed requests
            request.onload = function () {

                // Process our return data
                // status code between 200 and 300 indicates success
                if (request.status >= 200 && request.status < 300) {

                    console.log('success!', request);
                    console.log(request.responseText);

                    // continue with API calls.. 


                // you could set up a broader response handling if there is a use case for it
                } else {

                    console.log('The current token request failed!');

                }
            };

            request.send();                    

        }

    </script>

</body>