Skip to content

Conversation

@dmitrizagidulin
Copy link
Contributor

No description provided.

@dmitrizagidulin
Copy link
Contributor Author

cc @dan-f for review


describe('Unauthenticated requests to protected resources', () => {
describe('accepting json only', () => {
it('should return 401 Unauthorized with www-auth header', done => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW if this is mocha, you can just return promises from the test rather than explicitly call done.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True - this version of supertest doesn't support promises I think, hence the explicit done(). I was planning on upgrading to the latest soon anyway, though.

.set('Accept', 'text/html')
.expect('Content-Type', 'text/html; charset=utf-8')
.expect(res => {
expect(res.text).to.match(/<meta http-equiv="refresh"/)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where are we expected to be redirected to? I think that belongs with the test.

})

describe('with an expired bearer token', () => {
const expiredToken = 'eyJhbGciOiJSUzI1NiIsImtpZCI6ImxOWk9CLURQRTFrIn0.eyJpc3MiOiJodHRwczovL2xvY2FsaG9zdDozNDU3Iiwic3ViIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MzQ1Ny9wcm9maWxlL2NhcmQjbWUiLCJhdWQiOiJodHRwczovL2xvY2FsaG9zdDozNDU3IiwiZXhwIjoxNDk2MjM5ODY1LCJpYXQiOjE0OTYyMzk4NjUsImp0aSI6IjliN2MwNGQyNDY3MjQ1ZWEiLCJub25jZSI6IklXaUpMVFNZUmktVklSSlhjejVGdU9CQTFZR1lZNjFnRGRlX2JnTEVPMDAiLCJhdF9oYXNoIjoiRFpES3I0RU1xTGE1Q0x1elV1WW9pdyJ9.uBTLy_wG5rr4kxM0hjXwIC-NwGYrGiiiY9IdOk5hEjLj2ECc767RU7iZ5vZa0pSrGy0V2Y3BiZ7lnYIA7N4YUAuS077g_4zavoFWyu9xeq6h70R8yfgFUNPo91PGpODC9hgiNbEv2dPBzTYYHqf7D6_-3HGnnDwiX7TjWLTkPLRvPLTcsCUl7G7y-EedjcVRk3Jyv8TNSoBMeTwOR3ewuzNostmCjUuLsr73YpVid6HE55BBqgSCDCNtS-I7nYmO_lRqIWJCydjdStSMJgxzSpASvoeCJ_lwZF6FXmZOQNNhmstw69fU85J1_QsS78cRa76-SnJJp6JCWHFBUAolPQ'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is too opaque. The code should explicitly generate an expired token for the purposes of a test like this.

debug('401 error - redirect to Select Provider')
res.status(err.status)
redirectToLogin(req, res, next)
if (ldp.auth === 'oidc') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way of including this in the OIDC code rather than in the global error handler?

WWW-Authenticate is meant to be used for all 401s, so it should be here for WebID-TLS as well.

res.status(err.status)
redirectToLogin(req, res, next)
if (ldp.auth === 'oidc') {
let statusCode = err.status || err.statusCode || 500
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we've talked about this before, but I can't think of an instance in which, in application code, a 500 error code should be explicitly set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember. The reason I added it here is that this is the last place to catch it (if some other module forgot to specify a status code for their error), otherwise it gets a '0 is an invalid status code' error. I can take it out tho.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well I guess 500 is better than 0, hah.

.map(key => `${key}="${errorParams[key]}"`)
.join(', ')

res.set('WWW-Authenticate', 'Bearer ' + challengeParams)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This header is meant to be used with 401s, but it's being used here regardless of the error code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. (forgot to move it.)


res.set('WWW-Authenticate', 'Bearer ' + challengeParams)

if (statusCode === 401 && req.accepts('text/html')) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And if status code is anything else? what does the return at the end of this function actually do?

@dmitrizagidulin
Copy link
Contributor Author

@dan-f I rewrote the error-pages handler to reflect feedback, and added tests.

Copy link
Contributor

@dan-f dan-f left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking better. I still have a few points of review

return
}
let locals = req.app.locals
let authMethod = locals.authMethod
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does locals.authMethod come from? Does the client specify the auth method, or is it in the server configuration?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Server configuration.

})

describe('Authenticated responses to protected resources', () => {
describe('with an empty bearer token', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there anything in the error telling the client that they messed up the Authorization header? I'd expect a 400 in this case, since I think it could count as malformed syntax.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Bearer Token spec dictates treating an empty bearer token as a plain 401 Unauthorized (with no error). And a malformed token as a 401 with a invalid_token error (which is tested below).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh. According to section 3.1 of the spec, a missing bearer token seems to satisfy the invalid_request/400 scenario:

invalid_request
The request is missing a required parameter, includes an
unsupported parameter or parameter value, repeats the same
parameter, uses more than one method for including an access
token, or is otherwise malformed. The resource server SHOULD
respond with the HTTP 400 (Bad Request) status code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite - the bearer token is not a required parameter.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bearer token in bearer token authentication is not a required parameter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dan-f Changed to return an invalid_token / Empty access token error instead.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still needs to be 400, not 401.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I completely disagree with your interpretation, but since this seems to be an important issue for you, I will change it to a 400.

Copy link
Contributor

@dan-f dan-f Jun 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

invalid_request
The request is missing a required parameter, includes an
unsupported parameter or parameter value, repeats the same
parameter, uses more than one method for including an access
token, or is otherwise malformed. The resource server SHOULD
respond with the HTTP 400 (Bad Request) status code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dan-f now returning a 400 on an empty bearer token header.

res.header('Content-Type', 'text/html')
res.send(text)
res.status(statusCode)
res.header('Content-Type', 'text/html')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should really obey the Accept header.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be nice, agreed. I've opened issue #508 to implement support for more error formats (other than text/html) based on the Accept header.

@dmitrizagidulin
Copy link
Contributor Author

@dan-f any other issues on these 401 tests, or can I merge this?

@dmitrizagidulin dmitrizagidulin merged commit 2af40a0 into dz_oidc Jun 7, 2017
@dmitrizagidulin dmitrizagidulin deleted the www-auth-header branch June 7, 2017 15:21
dmitrizagidulin added a commit that referenced this pull request Jun 30, 2017
* Add WWW-Authenticate to exposed CORS headers

* Fix 401 error handling, add tests

* Add a test for expired token error

* Update to supertest 3.0.0

* Convert 401 tests to Promise interface

* Rewrite error-pages handler, add tests

* Change 401 error logic on empty bearer token

* Return a 400 error on empty bearer token header
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants