Skip to content

Commit

Permalink
feat: allow passing loginHint option to ensureAuthenticated
Browse files Browse the repository at this point in the history
add changelog

OKTA-448944
<<<Jenkins Check-In of Tested SHA: f608f95 for [email protected]>>>
Artifact: okta-oidc-middleware
Files changed count: 7
PR Link: "#40"
  • Loading branch information
oleksandrpravosudko-okta authored and eng-prod-CI-bot-okta committed Jan 24, 2022
1 parent 23bba66 commit 9c5e3b0
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 6 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# 4.5.0

### Features

- [#40](https://github.com/okta/okta-oidc-middleware/pull/34) Allows passing `loginHint` to `ensureAuthenticated`

# 4.4.0

### Bug Fixes
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ oidc.on('error', err => {
});
```

#### oidc.ensureAuthenticated({ redirectTo?: '/uri' })
#### oidc.ensureAuthenticated({ redirectTo?: '/uri', loginHint?: 'username' })

Use this to protect your routes. If not authenticated, this will redirect to the login route and trigger the authentication flow. If the request prefers JSON then a 401 error response will be sent.

Expand All @@ -229,6 +229,8 @@ app.get('/protected', oidc.ensureAuthenticated(), (req, res) => {

The `redirectTo` option can be used to redirect the user to a specific URI on your site after a successful authentication callback.

Passing `loginHint` option will append `login_hint` query parameter to URL when redirecting to Okta-hosted sign in page.

#### oidc.forceLogoutAndRevoke()

Use this to define a route that will force a logout of the user from Okta and the local session. Because logout involves redirecting to Okta and then to the logout callback URI, the body of this route will never directly execute. It is recommended to not perform logout on GET queries as it is prone to attacks and/or prefetching misadventures.
Expand Down
3 changes: 3 additions & 0 deletions src/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"env": {
"node": true
},
"parserOptions": {
"ecmaVersion": 2018
}
}
11 changes: 10 additions & 1 deletion src/connectUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ connectUtil.createOIDCRouter = context => {
};

connectUtil.createLoginHandler = context => {
const passportHandler = passport.authenticate('oidc');
const csrfProtection = csrf();
const ALLOWED_OPTIONS = ['login_hint'];

return function(req, res, next) {
const viewHandler = context.options.routes.login.viewHandler;
Expand Down Expand Up @@ -76,6 +76,15 @@ connectUtil.createLoginHandler = context => {
return res.redirect(authorizationUrl);
});
}
const options = Object.keys(req.query).reduce((opts, option) => {
return ALLOWED_OPTIONS.includes(option) ? {
...opts,
[option]: req.query[option]
} : {
...opts
}
}, {})
const passportHandler = passport.authenticate('oidc', options);
return passportHandler.apply(this, arguments);
}
};
Expand Down
14 changes: 12 additions & 2 deletions src/oidcUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ function customizeUserAgent(options) {
return options;
}

function appendOptionsToQuery(url, options) {
if (options.loginHint) {
const urlObject = new URL(url, 'relative:///');
const searchParams = urlObject.searchParams;
searchParams.append('login_hint', options.loginHint);
// extend original query (if any)
return `${url.split('?').shift()}${urlObject.search}`;
}
return url;
}

oidcUtil.createClient = context => {
const {
issuer,
Expand Down Expand Up @@ -133,9 +144,8 @@ oidcUtil.ensureAuthenticated = (context, options = {}) => {
if (req.session) {
req.session.returnTo = req.originalUrl || req.url;
}

const url = options.redirectTo || context.options.routes.login.path;
return res.redirect(url);
return res.redirect(appendOptionsToQuery(url, options));
}

next();
Expand Down
44 changes: 44 additions & 0 deletions test/unit/connectUtil.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const connectUtil = require('../../src/connectUtil.js');

jest.mock('csurf', function () {
return function () {

}
});

var mockAuthenticate;
jest.mock('passport', function () {
mockAuthenticate = jest.fn().mockReturnValue(() => {})
return {
authenticate: mockAuthenticate
}
})



describe('connectUtil', function () {
describe('createLoginHandler', function () {
it('passes known options to passport handler initializer', function () {
const loginHandler = connectUtil.createLoginHandler({
options: {
routes: {
login: {
}
}
}
});
const res = {};
const req = {
query: {
login_hint: '[email protected]',
chown_base: true
}
};

loginHandler(req, res);
expect(mockAuthenticate).toBeCalledWith('oidc', {
login_hint: '[email protected]'
});
});
});
});
29 changes: 27 additions & 2 deletions test/unit/oidcUtil.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@ const passport = require('passport');
const OpenIdClient = require('openid-client');
const oidcUtil = require('../../src/oidcUtil.js');

jest.mock('negotiator', function () {
return function () {
return {
mediaType: function () {
return 'text/html';
}
}
}
});

function createMockOpenIdClient(config={}) {
const Issuer = OpenIdClient.Issuer;

Expand Down Expand Up @@ -94,6 +104,21 @@ describe('oidcUtil', function () {
expect(error).toEqual(undefined);
};
passportStrategy.authenticate(createMockRedirectRequest());
})
})
});
});

describe('ensureAuthenticated', () => {
it('appends known options to redirect URL', () => {
const requestHandler = oidcUtil.ensureAuthenticated({}, {
redirectTo: '/login',
loginHint: '[email protected]'
});
let req = jest.mock();
let res = {
redirect: jest.fn()
};
requestHandler(req, res, () => {});
expect(res.redirect).toBeCalledWith('/login?login_hint=username%40org.org');
});
});
})

0 comments on commit 9c5e3b0

Please sign in to comment.