koa passport oauth2 save token to state
Asked Answered
E

2

0

So I'm trying to save an access token to the state within Koa, just for use later and saving having to pass it around the client.

Following the passport oauth2 documentation for Koa, I'm struggling to persist anything to ctx.state...

Koa / passport oauth2 setup:

server.koaApp.use(koaSession(
  {
    key: sessionKey,
    rolling: true,
    maxAge: sessionMaxAge,
    sameSite: 'none',
    secure: true,
    // @ts-ignore
    domain: undefined
  },
  server.koaApp,
));

// set up passport sessions here
function createPassport() {
  const passport = new KoaPassport();

  passport.use(new OAuth2Strategy({
    authorizationURL: oauthClientAuthUrl,
    tokenURL: oauthClientTokenUrl,
    clientID: oauthClientId,
    clientSecret: oauthClientSecret,
    callbackURL: oauthClientRedirectUrl,
  }, function(accessToken, refreshToken, params, profile, cb) {
    cb(null, {id: 'somedudesID', accessToken});
  }));

  passport.serializeUser((user, done) => {
    done(null, user);
  });

  passport.deserializeUser((user, done) => {
    done(null, user);
  });

  return passport;
};

Route declarations that should set and read accesstoken:

router.get('/authorize', passport.authenticate('oauth2'), (ctx: any) => {
        const { accessToken } = ctx.session.passport.user;
        ctx.state.accessToken = accessToken;
        ctx.status = 200;
      });

    router.get('/get-token-from-state', (ctx: any) => {
      console.log(ctx.state.accessToken); // undefined
    });

Questions:

  1. Why is ctx.state.accessToken undefined when doing a get to /get-token-from-state?

  2. Should I even be trying to persist the access token like this? Is there another way to obtain the accessToke in other routes?

Enyo answered 16/3, 2020 at 16:41 Comment(0)
E
0

So, answering my own question here, it was a number of things:

  1. "sameSite: 'lax'" was needed
  2. Cookies of the session were not being saved in browser because the domains were slightly off (port numbers were different.

I was able to proxy this locally and all good!

Enyo answered 29/3, 2020 at 13:23 Comment(0)
M
1

I had the same annoying experience. What helped was removing koa-session and replace it with koa-generic-session. Then I setup a memory store -> now it works :)

Iam not sure if storing the token inside a memory session is the best idea - but right now its my first draft. Its only a little code block to get in touch with keycloak.

server.js

    var app = new Koa();
    app.keys = ['keys', 'keykeys'];
    var memoryStore = new session.MemoryStore();

    // Session
    app.use(session({
        secret: 'some secret',
        resave: false,
        saveUninitialized: true,
        store: memoryStore
      }));

    // Passport
    const passport = require('./oauth2')
    app.use(passport.initialize());
    app.use(passport.session());

oauth2.js

const request = require('request');
const passport = require('koa-passport');
const OAuth2Strategy = require('passport-oauth2').Strategy;


const clientId = "b2m";
const clientSecrect = "57ce5bba-f336-417f-b9c2-06157face88f";

OAuth2Strategy.prototype.userProfile = function (accessToken, done) {
  var options = {
      url: 'http://localhost:8080/auth/realms/master/protocol/openid-connect/userinfo',
      headers: {
          'User-Agent': 'request',
          'Authorization': 'Bearer ' + accessToken,
      }
  };

  request(options, callback);

  function callback(error, response, body) {
      if (error || response.statusCode !== 200) {
          return done(error);
      }
      var info = JSON.parse(body);
      return done(null, info);
  }
};

passport.use(new OAuth2Strategy({
    authorizationURL: 'http://localhost:8080/auth/realms/master/protocol/openid-connect/auth',
    tokenURL: 'http://localhost:8080/auth/realms/master/protocol/openid-connect/token',
    clientID: clientId,
    clientSecret: clientSecrect,
    callbackURL: "http://localhost:3000/callback"
  },
  function(accessToken, refreshToken, profile, cb) {
    console.log('#########################################################');
    console.log('Authenticated with OAuth2');
    console.log('accessToken', accessToken);
    console.log('refreshToken', refreshToken);
    
    var user = {
      accessToken: accessToken,
      refreshToken: refreshToken,
      profile: profile
    };
    console.log('user', user);

    return cb(null, user);
  }
));

/* Example: storing user data received from the strategy callback in the session, i.e. in `req.session.passport.user` */
passport.serializeUser(function(user, done) {
    done(null, user);
  });
  
/* Example: getting the user data back from session and attaching it to the request object, i.e. to `req.user` */
passport.deserializeUser(function (user, next) {
    /*
      Example: if only a user identifier is stored in the session, this is where
      the full set could be retrieved, e.g. from a database, and passed to the next step
    */
    next(null, user);
});

module.exports = passport;
Meter answered 18/3, 2020 at 11:58 Comment(1)
Thanks @Axel! I'm a little further along with your approach but still unable to pull the user from ctx.state or ctx.session. Logs: ctx.state = { _passport: { instance: KoaPassport { _key: 'passport', _strategies: [Object], _serializers: [Array], _deserializers: [Array], _infoTransformers: [], _framework: [Object], _userProperty: 'user', _sm: [SessionManager] } } } ctx.session = { cookie: { httpOnly: true, path: '/', overwrite: true, signed: true, maxAge: 86400000 } }Enyo
E
0

So, answering my own question here, it was a number of things:

  1. "sameSite: 'lax'" was needed
  2. Cookies of the session were not being saved in browser because the domains were slightly off (port numbers were different.

I was able to proxy this locally and all good!

Enyo answered 29/3, 2020 at 13:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.