Verifying ID token from OIDC Auth Flow

I’m able to decode the ID token just fine, using https://jwt.io/#libraries and also with the corresponding npm jsonwebtoken package from that website.

I am unable to verify the ID token. Using the jwt website, I plug in the key from https://id.twitch.tv/oauth2/keys into the public key bin (It looks like it accepts a B64 encoded text, but I tried with and without) .

(key is in bold {“keys”:[{“alg”:“RS256”,“e”:“AQAB”,“kid”:“1”,“kty”:“RSA”,“n”:“6lq9MQ-q6hcxr7kOUp-tHlHtdcDsVLwVIw13iXUCvuDOeCi0VSuxCCUY6UmMjy53dX00ih2E4Y4UvlrmmurK0eG26b-HMNNAvCGsVXHU3RcRhVoHDaOwHwU72j7bpHn9XbP3Q3jebX6KIfNbei2MiR0Wyb8RZHE-aZhRYO8_-k9G2GycTpvc-2GBsP8VHLUKKfAs2B6sW3q3ymU6M0L-cFXkZ9fHkn9ejs-sqZPhMJxtBPBxoUIUQFTgv4VXTSv914f_YkNw-EjuwbgwXMvpyr06EyfImxHoxsZkFYB-qBYHtaMxTnFsZBr6fn8Ha2JqT1hoP7Z5r5wxDu3GQhKkHw”,“use”:“sig”}]})

I also have some confusion about the npm jsonwebtokens package, as the verify function accepts the ID token and a public key, yet I don’t see an option for a private key (which I would think is mandatory as RS256 accepts a public and private key).

Any assistance would be much appreciated!

I am using https://www.npmjs.com/package/idtoken-verifier for OIDC so try this instead

OIDC doesn’t quite return a jwt.io style token.

1 Like

Ok thanks again Barry, your time is much appreciated over here!

I’m using that npm package you posted.The verify function is giving null for the error field, and the decoded ID_Token for payload field. Does this imply verification?

For future observers of this message that decide to use this package, be sure to pass a nonce parameter on your initial OIDC authorization call (You should see a nonce field in your corresponding decoded ID_Token).

Also make sure you provide a direct link to the JWK as follows,

var verifier = new IdTokenVerifier({
	    issuer: issuer,
	    audience: clientID,
	    jwksURI:'https://id.twitch.tv/oauth2/keys'
	});

I didn’t want to make it toooooo easy for you…

Heres a more useful/rubbishy example of using OIDC (I’ll probably submit this as a docs example when cleaned up)

const request = require('request');
const IdTokenVerifier = require('idtoken-verifier');

/* OIDC */
let oidc_data;
let verifier;

request.get({
    url: 'https://id.twitch.tv/oauth2/.well-known/openid-configuration',
    json: true
}, function(e, r, b) {
    if (e) {
        console.log(e);
        process.exit();
    } else if (r.statusCode == 200) {
        console.log('Got openid config');
        oidc_data = b;

        verifier = new IdTokenVerifier({
            issuer: oidc_data.issuer,
            audience: config.twitch.client,
            jwksURI: oidc_data.jwks_uri
        });
    } else {
        console.log('Got a ' + r.statusCode);
        process.exit();
    }
});

/* SNIP */

app.get('/login/', function(req, res) {
    var error = req.query.error ? req.query.error : false;
    if (error) {
        res.render('error', { error: {error: 'Twitch Hiccuped! ' + error} });
    } else {
        var code = req.query.code ? req.query.code : false;
        var scope = req.query.scope ? req.query.scope : false;
        var state = req.query.state ? req.query.state : false;

        if (code) {
            request.post({
                url: oidc_data.token_endpoint,
                headers: {
                    'Accept': 'application/json'
                },

                body: {
                    client_id: config.twitch.client,
                    client_secret: config.twitch.secret,
                    code: code,
                    grant_type: 'authorization_code',
                    redirect_uri: config.twitch.redirect
                },

                gzip: true,
                json: true
            }, function(e, r, b) {
                if (e) {
                    console.log(e);

                    res.render('error', { error: {error: 'Twitch Hiccuped!'} });

                    return;
                } else if (r.statusCode == 200) {
                    req.session.twitch = b;
                    req.session.user = {};

                    verifier.verify(b.id_token, null, function(error, payload) {
                        if (error) {
                            console.log('Error', error);
                            res.render('error', { error: {error: 'twitch hiccuped!'} });
                        } else {
                            console.log('Login from', payload.sub);

                            request.post({
                                url: oidc_data.userinfo_endpoint,
                                headers: {
                                    'Accept': 'application/json',
                                    'Authorization': 'Bearer ' + req.session.twitch.access_token,
                                },

                                gzip: true,
                                json: true
                            }, function(e, r, b) {
                                if (e) {
                                    console.log(e);
                                    res.render('error', { error: {error: 'twitch hiccuped!'} });
                                } else if (r.statusCode == 200) {
                                    //console.log('headers', r.headers);
                                    console.log('Userinfo', b);

                                    req.session.user = b;

                                    res.redirect('/');

                                    return;
                                } else {
                                    console.log('Failed on userinfo_endpoint', b);
                                    res.render('error', { error: {error: 'twitch hiccuped!'} });
                                }
                            });
                        }
                    });

                } else {
                    console.log('Token r: ' + r.statusCode);
                    res.render('error', { error: {error: 'twitch hiccuped!'} });

                    return;
                }
            });

            return;
        }

        var url = oidc_data.authorization_endpoint
            + '?client_id=' + config.twitch.client
            + '&redirect_uri=' + config.twitch.redirect
            + '&response_type=code'
            + '&force_verify=true'
            + '&scope=' + oidc_data.scopes_supported.join('+')
            + '&claims=' + JSON.stringify({
                userinfo: {
                    email:null,
                    email_verified:null,
                    picture:null,
                    preferred_username:null
                }
            });

        res.redirect(url);
    }
});

TLDR: init IdTokenVerifier with data from https://id.twitch.tv/oauth2/.well-known/openid-configuration

1 Like

Yeah that’s a nice example, it’s even just a small few snippets away from a base template app in node.js (client-sessions and clusters, which I assume has something to do with your process.exit() statements).

Thanks for the clarification.

I process.exit() to just dump the whole program out and let GOD handle a restart.

Since my fetch of the OIDC config is performed at boot only.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.