Alexa auth works in Postman but doesn't work in the Alexa test

60 Views Asked by At

My authentication code is only not working on Alexa... When I test it, it only returns 'Cannot link at the moment,' but when I test it on Postman, it returns the linking token perfectly.

var oauth2orize = require('oauth2orize')
var User = require('../models/User');
var Client = require('../models/Client');
var Token = require('../models/Token');
var Code = require('../models/Code');

// Create OAuth 2.0 server
var server = oauth2orize.createServer();

// Register serialialization function
server.serializeClient(function (client, callback) {
    return callback(null, client._id);
});

// Register deserialization function
server.deserializeClient(function (id, callback) {
    Client.findOne({ _id: id }, function (err, client) {
        if (err) { return callback(err); }

        return callback(null, client);
    });
});

// Register authorization code grant type
server.grant(oauth2orize.grant.code({ scopeSeparator: [ ' ', ',' ] },
function (client, redirectUri, user, ares, callback) {

    // Create a new authorization code
    var code = new Code({
        value: uid(16),
        clientId: client._id,
        redirectUri: redirectUri,
        usermail: user.email
    });

    // Save the auth code and check for errors
    code.save(function (err) {
        if (err) { return callback(err); }

        callback(null, code.value);
    });
}));

// Exchange authorization codes for access tokens
server.exchange(oauth2orize.exchange.code(function (client, code, redirectUri, callback) {
    console.log("GENERATING TOKEN.....")

    Code.findOne({ value: code }, function (err, authCode) {
        if (err) { return callback(err); }

        if (client === undefined || authCode === undefined) { return callback(null, false); }

        if (client._id.toString() !== authCode.clientId) { return callback(null, false); }

        if (redirectUri !== authCode.redirectUri) { return callback(null, false); }

        // Delete auth code now that it has been used
        authCode.remove(function (err) {

            if (err) { return callback(err); }

            // Create a new access token
            var token = new Token({
                value: uid(256),
                refresh: uid(256),
                clientId: authCode.clientId,
                usermail: authCode.usermail
            });

            // Save the access token and check for errors
            token.save(function (err) {
                if (err) { return callback(err); }
                callback(null, token.value, token.refresh, {expires_in: 3600});
            });
        });
    });
}));

server.exchange(oauth2orize.exchange.refreshToken(function (client, refreshToken, scope, callback) {
    console.log("REFRESHING TOKEN.....")

    Token.findOne({refresh: refreshToken }, (err, token) => {
        if (err) { console.log("ERROR!"); return callback(err); }

        if (!token) { console.log("INVALID TOKEN!"); return callback(null, false); }

        token.remove(function (err) {
            if (err) { return callback(err); }

            // Create a new access token
            var newToken = new Token({
                value: uid(256),
                refresh: uid(256),
                clientId: token.clientId,
                usermail: token.usermail
            });

            // Save the access token and check for errors
            newToken.save(function (err) {
                if (err) { return callback(err); }
                callback(null, newToken.value, newToken.refresh, {expires_in: 3600});
            });
        });
    });
}));

// User authorization endpoint
exports.authorization = [
    server.authorization(function (clientId, redirectUri, callback) {
        console.log("OAUTH2 AUTHORIZATION!");
        Client.findOne({ id: clientId }, function (err, client) {
            if (err) { return callback(err); }
            
            return callback(null, client, redirectUri);
        });
    }),

    function (req, res) {
        let clz = ""
        if(req.oauth2.client.name.includes("Google")) clz = "home"
        else if(req.oauth2.client.name.includes("Alexa")) clz = "alexa"
    
        res.render('dialog', { transactionID: req.oauth2.transactionID, 
            scopes: req.oauth2.req.scope, 
            currentURL: encodeURIComponent(req.originalUrl),
            response_type: req.query.response_type,
            user: req.user, 
            client: req.oauth2.client, 
            clazz: clz 
        });
    }
]

// User decision endpoint
exports.decision = [
    server.decision(function(req, done){
        done(null, { scope: req.oauth2.req.scope });
    })
]

// Application client token exchange endpoint
exports.token = [
    server.token(),
    server.errorHandler()
]

// Utility functions to generate unique identifiers
function uid (len) {
    var buf = []
      , chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
      , charlen = chars.length;
    for (var i = 0; i < len; ++i) {
      buf.push(chars[getRandomInt(0, charlen - 1)]);
    }
    return buf.join('');
  };

  function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
  }

I expected to receive the linking token in my MongoDB database along with the account and information from my API.

1

There are 1 best solutions below

0
Ilyt_A On

I recommend you to go through the following steps:

  1. Check your authorization server for logs -More often than not, account linking fails due to an error sent by your authorization server. Check the responses from your authorization server to see what you are sending back to Alexa when your authorization server is presented with the authorization code in exchange for an access/refresh token pair. -If you don't have access to the service you are account linking with, it would be a good idea at this point to get access to additional logs. A good way to do this is via this excellent guide on setting up AWS API gateway as an intermediary:

https://developer.amazon.com/blogs/post/TxQN2C04S97C0J/how-to-set-up-amazon-api-gateway-as-a-proxy-to-debug-account-linking

  1. For smart home skills, check your AcceptGrant response -If you are building a smart home skill and using account linking, you might have the "Send Alexa Events" permissions turned on within the Developer Console for your skill so that you can send events to the Alexa Event Gateway. If so, you need to respond to the AcceptGrant directive that is sent to your skill during account linking. More details here:

https://developer.amazon.com/docs/alexa/device-apis/alexa-authorization.html#directives

  1. Troubleshoot using the Alexa app in your browser -When enabling skills on your account, you can use the Alexa app in your browser. The URL of the Alexa app will change depending on the region set in your account. For example:

US - alexa.amazon.com UK - alexa.amazon.co.uk JP - alexa.amazon.co.jp

-You can test account linking like you would on your mobile Alexa app, but with the addition of developer tools and debuggers on your browser. This will let you view the network requests going back and forth for account linking, and you may notice a parameter or some other issue you may have missed. You especially want to check to make sure the state parameter stays the same throughout the account linking process.

  1. Check that your server is responding within 4.5 seconds -When your authorization server is responding to the access token request, your server needs to respond within 4.5 seconds. If it doesn't, the request will timeout and account linking won't succeed. For other requirements for the access token requirements, check out further details here: https://developer.amazon.com/docs/alexa/account-linking/requirements-account-linking.html#access-token-uri-requirements

For more information please follow this troubleshooting guide: https://amazon.developer.forums.answerhub.com/articles/38610/alexa-debugging-account-linking.html