Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

userinfo w/ www-authenticate but no body error responses don't throw OpenIdConnectError #102

Closed
danedmunds opened this issue Jun 28, 2018 · 12 comments

Comments

@danedmunds
Copy link

I might be wrong in my interpretation of the specs here but I think OIDC providers are supposed to be able to just use the WWW-Authenticate header value on an error to determine the error code.

I'm looking at the example here http://openid.net/specs/openid-connect-core-1_0.html#rfc.section.5.3.3

I'm thinking the following test should succeed when added to https://github.com/panva/node-openid-client/blob/master/test/client/client_instance.test.js

      it('is rejected with OpenIdConnectError upon oidc error in www-authenticate header', function () {
        const issuer = new Issuer({ userinfo_endpoint: 'https://op.example.com/me' });
        const client = new issuer.Client();

        nock('https://op.example.com')
          .get('/me')
          .reply(401, '', {
            'WWW-Authenticate': 'error="invalid_token", error_description="The Access Token expired"'
          });

        return client.userinfo()
          .then(fail, function (error) {
            expect(error.name).to.equal('OpenIdConnectError');
            expect(error).to.have.property('error', 'invalid_token');
          });
      });

If you're ok with this idea I don't mind implementing and issuing a PR

Thanks!

@panva
Copy link
Owner

panva commented Jun 28, 2018

Hi! Which provider is omitting a body? I had to dive in the bearer token usage spec and a body must be returned too.

www-authenticate is then defined in addition only for 401 responses as a MUST (not sure) and optional for all other.

edit: hmm, you might be right. let me check some more

@danedmunds
Copy link
Author

No specific provider is doing this, it was just something I noticed while writing tests for my component (I was just returning the header as a userinfo rejection) and thought I'd bring it up.

Like I said at the start though, I may totally be wrong, I trust your interpretation of the specs way more ;)

@panva
Copy link
Owner

panva commented Jun 28, 2018

haha, it's worth checking out, i reached out for some help on this and also believe you are correct

@panva
Copy link
Owner

panva commented Jun 28, 2018

oh boy, please help... I hate this :) are you familiar with the www-authenticate header syntax in regards to token values, is " optional?, how are " escaped inside token values? I'm getting a headache...

error handler

const isStandardBodyError = require('./is_standard_body_error');
const checkIfBearerHeaderOnlyError = require('./is_bearer_header_only_error');
const OpenIdConnectError = require('./open_id_connect_error');

module.exports = function requestErrorHandler(err) {
  if (isStandardBodyError.call(this, err)) {
    throw new OpenIdConnectError(err.response.body, err.response);
  }

  const [isBearerHeaderOnlyError, params] = checkIfBearerHeaderOnlyError.call(this, err);

  if (isBearerHeaderOnlyError) {
    throw new OpenIdConnectError(params, err.response);
  }

  throw err;
};

checkIfBearerHeaderOnlyError

// REGEXP shamelessly ripped of the interwebs
const REGEXP = /((?:[a-zA-Z0-9._~+/-]+=*(?:\s+|$))|[^()<>@,;:\\"/?={}[\] ]+)(?:=([^\\"=\s,]+|"(?:[^"\\]|\\.)*"))?/g;
const NOPE = [false];

module.exports = function isBearerHeaderOnlyError(error) {
  if (error instanceof this.httpClient.HTTPError) {
    try {
      const body = {};
      if (!error.response.headers['www-authenticate'].startsWith('Bearer ')) {
        return NOPE;
      }

      while ((REGEXP.exec(error.response.headers['www-authenticate'])) !== null) {
        if (RegExp.$1 && RegExp.$2) {
          body[RegExp.$1] = RegExp.$2.slice(1, -1);
        }
      }

      return [!!body.error, body];
    } catch (err) {}
  }

  return NOPE;
};

@panva panva changed the title WWW-Authenticate headered but bodiless userinfo error responses don't throw OpenIdConnectError userinfo w/ www-authenticate but no body error responses don't throw OpenIdConnectError Jun 28, 2018
@danedmunds
Copy link
Author

Oh man, definitely not familiar but I'll try to do some digging myself too!

@danedmunds
Copy link
Author

REGEXP shamelessly ripped of the interwebs 😆

@danedmunds
Copy link
Author

danedmunds commented Jun 28, 2018

I just tested against Auth0

> curl -v -H "Authorization: Bearer 12345654321" https://danedmunds.auth0.com/userinfo
*   Trying 52.89.53.165...
* Connected to danedmunds.auth0.com (52.89.53.165) port 443 (#0)
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate: *.auth0.com
* Server certificate: Amazon
* Server certificate: Amazon Root CA 1
* Server certificate: Starfield Services Root Certificate Authority - G2
> GET /userinfo HTTP/1.1
> Host: danedmunds.auth0.com
> User-Agent: curl/7.43.0
> Accept: */*
> Authorization: Bearer 12345654321
>
< HTTP/1.1 401 Unauthorized
< Date: Thu, 28 Jun 2018 17:53:39 GMT
< Content-Length: 12
< Connection: keep-alive
< X-Auth0-RequestId: 584f99a5ba4b015c1b79
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Credentials: false
< WWW-Authenticate: Bearer realm="Users", error="invalid_token", error_description="The access token signature could not be validated. A common cause of this is requesting multiple audiences for an access token signed with HS256, as that signature scheme requires only a single recipient for its security. Please change your API to employ RS256 if you wish to have multiple audiences for your access tokens"
< Cache-Control: private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0
<
* Connection #0 to host danedmunds.auth0.com left intact
Unauthorized

Looks like they don't return the json body, only the WWW-Authenticate header 😢

@panva
Copy link
Owner

panva commented Jun 28, 2018

@auth0++ :)

@danedmunds
Copy link
Author

Okta too

curl -v -H "Authorization: Bearer 12345654321" https://dev-927219.oktapreview.com/oauth2/v1/userinfo
*   Trying 50.17.226.139...
* Connected to dev-927219.oktapreview.com (50.17.226.139) port 443 (#0)
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate: *.oktapreview.com
* Server certificate: DigiCert SHA2 High Assurance Server CA
* Server certificate: DigiCert High Assurance EV Root CA
> GET /oauth2/v1/userinfo HTTP/1.1
> Host: dev-927219.oktapreview.com
> User-Agent: curl/7.43.0
> Accept: */*
> Authorization: Bearer 12345654321
>
< HTTP/1.1 401 Unauthorized
< Date: Thu, 28 Jun 2018 17:59:54 GMT
< Server: nginx
< Public-Key-Pins-Report-Only: pin-sha256="jZomPEBSDXoipA9un78hKRIeN/+U4ZteRaiX8YpWfqc="; pin-sha256="axSbM6RQ+19oXxudaOTdwXJbSr6f7AahxbDHFy3p8s8="; pin-sha256="SE4qe2vdD9tAegPwO79rMnZyhHvqj3i5g1c2HkyGUNE="; pin-sha256="ylP0lMLMvBaiHn0ihLxHjzvlPVQNoyQ+rMiaj0da/Pw="; max-age=60; report-uri="https://okta.report-uri.io/r/default/hpkp/reportOnly"
< Content-Length: 0
< X-Okta-Request-Id: WzUiGpX@JRbqLffskRVMHgAADSo
< P3P: CP="HONK"
< X-Okta-backend: op1-majorapp04e.aue1s.internal
< Access-Control-Expose-Headers: WWW-Authenticate
< WWW-Authenticate: Bearer authorization_uri="http://dev-927219.oktapreview.com/oauth2/v1/authorize", realm="http://dev-927219.oktapreview.com", scope="openid", error="invalid_token", error_description="The access token is invalid.", resource="/oauth2/v1/userinfo"
< Content-Language: en
< Set-Cookie: sid=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/
<
* Connection #0 to host dev-927219.oktapreview.com left intact

@panva panva closed this as completed in c1aa481 Jun 28, 2018
@panva
Copy link
Owner

panva commented Jun 28, 2018

Thank you @danedmunds, this was a great find!

@danedmunds
Copy link
Author

Thanks @panva! Thanks for fixing it so quick too!
I was going to offer my services to help out but it's done already! 👍 😄

@panva
Copy link
Owner

panva commented Jun 28, 2018

no worries. btw using your examples i found an issue with one of the providers :) according to the spec if i provide no bearer authentication (no query, no body, no header) then the www-authenticate header should not contain details about the error and simply only contain a realm. one of the providers returns all details regardless :)

@github-actions github-actions bot locked and limited conversation to collaborators Apr 5, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants