Quarkus: OpenID Connect Extension with PingFederate

Created on 25 Jan 2020  路  13Comments  路  Source: quarkusio/quarkus

Describe the bug
Apologies if this is not a bug....
Working with OpenID Connect Extension to protect a Web Application (with Qute Template framework).
When configured for Ping Federate OpenID and trying to access a secured resource, I am getting a Status Code: 401 Unauthorized response from Auth provider.

Expected behavior
Status Code: 200 from Identity Provider and able to access secured resource with back to redirect_uri: http://localhost:8080/secure

Actual behavior
Status Code: 401 Unauthorized response
While I am not seeing detailed errors with logging enabled, I see the following with SSL Debug shows the following:
"error_description":"The client MUST NOT use more than one authentication method in each request.","error":"invalid_client"

To Reproduce
Steps to reproduce the behavior:

  1. Configure application.properties for PingFederate with scope of openid (as noted in the configuration below)
  2. Start JVM with -Djavax.net.debug=all and mvn compile quarkus:dev
  3. Access secured web application resource
  4. Review STDOUT logs

Configuration

# application.properties 

# Authentication Provider - Ping
quarkus.oidc.auth-server-url=https://ssaasi.${MY_DOMAIN}.com
quarkus.oidc.introspection-path=.well-known/openid-configuration
quarkus.oidc.authentication.redirect-path=/secure
quarkus.oidc.authentication.scopes=openid,profile,address,email,phone
quarkus.oidc.client-id=${PING_OPENID_CLIENT_ID}
quarkus.oidc.credentials.secret=${PING_OPENID_CLIENT_SECRET}
quarkus.oidc.application-type=WEB_APP

### Authentication Details
quarkus.http.auth.form.login-page=/secure
quarkus.http.auth.form.error-page=/error
# Allow - to all
quarkus.http.auth.permission.open.paths=/,/favicon.ico,/home,/login,/metrics*,/images/*,/error*,/bluegreen-check,/css/*
quarkus.http.auth.permission.open.policy=permit

### Allow - to authenticated
quarkus.http.auth.permission.authenticated.paths=/*
quarkus.http.auth.permission.authenticated.policy=authenticated

Screenshots
N/A

Environment (please complete the following information):

  • Output of uname -a or ver:
    ```shell
    Darwin MacBook-Pro.local 19.2.0 Darwin Kernel Version 19.2.0: Sat Nov 9 03:47:04 PST 2019; root:xnu-6153.61.1~20/RELEASE_X86_64 x86_64
 - Output of `java -version`: 
 ```shell
java -version
openjdk version "1.8.0_232"
OpenJDK Runtime Environment (build 1.8.0_232-20191009173705.graal.jdk8u-src-tar-gz-b07)
OpenJDK 64-Bit GraalVM CE 19.3.0 (build 25.232-b07-jvmci-19.3-b05, mixed mode)
  • GraalVM version (if different from Java): same
  • Quarkus version or git rev:
    From pom.xml
    quarkus.platform.version 1.1.1.Final

Additional context

Prior to final 401 response, I see the following 302 redirects in the brower:

  1. https://${MY_HOST}.${MY_DOMAIN}.com/as/authorization.oauth2 ... 302 to
  2. https://${MY_HOST}.${MY_DOMAIN}.com/as/rdivJ/resume/as/authorization.ping ... 302 to...
  3. http://localhost:8080/secure?code=..... 302 to
  4. https://${MY_HOST}.${MY_DOMAIN}.com/as/authorization.oauth2? ... 302 to
  5. https://${MY_HOST}.${MY_DOMAIN}.com/as/ZfXOn/resume/as/authorization.ping <---- 401

Our Ping well-known/openid-configuration looks like the following:

{
  "issuer": "https://${MY_HOST}.${MY_DOMAIN}.com",
  "authorization_endpoint": "https://${MY_HOST}.${MY_DOMAIN}.com/as/authorization.oauth2",
  "token_endpoint": "https://${MY_HOST}.${MY_DOMAIN}.com/as/token.oauth2",
  "revocation_endpoint": "https://${MY_HOST}.${MY_DOMAIN}.com/as/revoke_token.oauth2",
  "userinfo_endpoint": "https://${MY_HOST}.${MY_DOMAIN}.com/idp/userinfo.openid",
  "introspection_endpoint": "https://${MY_HOST}.${MY_DOMAIN}.com/as/introspect.oauth2",
  "jwks_uri": "https://${MY_HOST}.${MY_DOMAIN}.com/pf/JWKS",
  "registration_endpoint": "https://${MY_HOST}.${MY_DOMAIN}.com/as/clients.oauth2",
  "ping_revoked_sris_endpoint": "https://${MY_HOST}.${MY_DOMAIN}.com/pf-ws/rest/sessionMgmt/revokedSris",
  "ping_end_session_endpoint": "https://${MY_HOST}.${MY_DOMAIN}.com/idp/startSLO.ping",
  "device_authorization_endpoint": "https://${MY_HOST}.${MY_DOMAIN}.com/as/device_authz.oauth2",
  "scopes_supported": [ "some-int", "address", "edit", "openid", "profile", "admin", "orginfo", "${MY_DOMAIN}profile", "something", "${MY_DOMAIN}OIDCApps", "phone", "restricted-data", "some-dvl", "email", "confidential-data" ],
  "claims_supported": [ "myid", "email", "employeeID", "family_name", "given_name", "group_info", "middle_name", "name", "pi.sri", "pr_number", "preferred_username", "sub", "updated_at", "user_id" ],
  "response_types_supported": [ "code", "token", "id_token", "code token", "code id_token", "token id_token", "code token id_token" ],
  "response_modes_supported": [ "fragment", "query", "form_post" ],
  "grant_types_supported": [ "implicit", "authorization_code", "refresh_token", "password", "client_credentials", "urn:pingidentity.com:oauth2:grant_type:validate_bearer", "urn:ietf:params:oauth:grant-type:jwt-bearer", "urn:ietf:params:oauth:grant-type:saml2-bearer", "urn:ietf:params:oauth:grant-type:device_code", "urn:openid:params:grant-type:ciba" ],
  "subject_types_supported": [ "public", "pairwise" ],
  "id_token_signing_alg_values_supported": [ "none", "HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "ES256", "ES384", "ES512" ],
  "token_endpoint_auth_methods_supported": [ "client_secret_basic", "client_secret_post", "private_key_jwt" ],
  "token_endpoint_auth_signing_alg_values_supported":  [ "RS256", "RS384", "RS512", "ES256", "ES384", "ES512" ],
  "claim_types_supported": [ "normal" ],
  "claims_parameter_supported": false,
  "request_parameter_supported": true,
  "request_uri_parameter_supported": false,
  "request_object_signing_alg_values_supported": [ "RS256", "RS384", "RS512", "ES256", "ES384", "ES512" ],
  "id_token_encryption_alg_values_supported": [ "dir", "A128KW", "A192KW", "A256KW", "A128GCMKW", "A192GCMKW", "A256GCMKW", "ECDH-ES", "ECDH-ES+A128KW", "ECDH-ES+A192KW", "ECDH-ES+A256KW", "RSA-OAEP" ],
  "id_token_encryption_enc_values_supported": [ "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512", "A128GCM", "A192GCM", "A256GCM" ],
  "backchannel_authentication_endpoint": "https://${MY_HOST}.${MY_DOMAIN}.com/as/bc-auth.ciba",
  "backchannel_token_delivery_modes_supported": [ "poll", "ping" ],
  "backchannel_authentication_request_signing_alg_values_supported": [ "RS256", "RS384", "RS512", "ES256", "ES384", "ES512" ],
  "backchannel_user_code_parameter_supported": false
}

Side Note:
I have been able to successfully leverage Quarkus OpenID Connect with Google using the following:

# Authentication Provider - Google
quarkus.oidc.auth-server-url=https://accounts.google.com
quarkus.oidc.introspection-path=.well-known/openid-configuration
quarkus.oidc.authentication.redirect-path=/secure
quarkus.oidc.authentication.scopes=openid, email, profile
quarkus.oidc.client-id=${GOOGLE_OPENID_CLIENT_ID}
quarkus.oidc.credentials.secret=${GOOGLE_OPENID_CLIENT_SECRET}
quarkus.oidc.application-type=WEB_APP
areoidc areoidc-interoperability kinbug

Most helpful comment

@aaronanderson and @sberyozkin . I decided to be late to work a few mins and test it out, and THAT DID IT. Moving to 1.2.0.Final fixed it - Seems to work with PingFederate now! Thanks, everyone.

All 13 comments

@jessegduke Hi, thanks for this report, this issue has been fixed in the Quarkus master, if you can find the time and confirm it then it would be good, please get the source and rebuild it locally and retry with the SNAPSHOT, thanks

@sberyozkin Thanks for the quick response. Yes, I will try and do that this weekend.

Downloaded master this morning and built successfully on Mac (details above). Updated applicaiton pom.xml to 999-SNAPSHOT and quarkus-bom (from quarkus-universe-bom). At this time, I am still seeing the same error error_description":"The client MUST NOT use more than one authentication method in each request.","error":"invalid_client"

I see a number of quarkus application-level 302 redirects requesting from PingFederate a new response_type=code after having already received one successfully. (However, no HTTP 401 in the browser or logging (only via -Djavax.net.debug=all logging)

Some additional details:

Local App repetitive 302 redirects until my account is locked:

'redirectURL': 

'https://${MY_PING_HOST}.${MY_DOMAIN}.com/as/authorization.oauth2?redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fstart&state=${TRUNCATED FOR SECURITY}&scope=openid+profile+address+email+phone&response_type=code&client_id=${MY_APP}'

Request to Ping as captured from Chrome Browser Developer Tools:

    {
        "startedDateTime": "2020-01-25T13:05:05.645Z",
        "time": 123.1629999820143,
        "request": {
          "method": "GET",
          "url": "https://${MY_PING_HOST}.${MY_DOMAIN}.com/as/authorization.oauth2?redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fsecure&state=${TRUNCATED FOR SECURITY}&scope=openid+profile+address+email+phone&response_type=code&client_id=${MY_APP}",
          "httpVersion": "HTTP/1.1",
          "headers": [
            {
              "name": "Host",
              "value": "${MY_PING_HOST}.${MY_DOMAIN}.com"
            },
            {
              "name": "Connection",
              "value": "keep-alive"
            },
            {
              "name": "Pragma",
              "value": "no-cache"
            },
            {
              "name": "Cache-Control",
              "value": "no-cache"
            },
            {
              "name": "Upgrade-Insecure-Requests",
              "value": "1"
            },
            {
              "name": "DNT",
              "value": "1"
            },
            {
              "name": "User-Agent",
              "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36"
            },
            {
              "name": "Sec-Fetch-User",
              "value": "?1"
            },
            {
              "name": "Accept",
              "value": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
            },
            {
              "name": "Sec-Fetch-Site",
              "value": "cross-site"
            },
            {
              "name": "Sec-Fetch-Mode",
              "value": "navigate"
            },
            {
              "name": "Referer",
              "value": "https://${MY_PING_HOST}.${MY_DOMAIN}.com/"
            },
            {
              "name": "Accept-Encoding",
              "value": "gzip, deflate, br"
            },
            {
              "name": "Accept-Language",
              "value": "en-US,en;q=0.9"
            },
            {
              "name": "Cookie",
              "value": "${TRUNCATED FOR SECURITY}"
            }
          ],
          "queryString": [
            {
              "name": "redirect_uri",
              "value": "http%3A%2F%2Flocalhost%3A8080%2Fsecure"
            },
            {
              "name": "state",
              "value": "${TRUNCATED FOR SECURITY}"
            },
            {
              "name": "scope",
              "value": "openid+profile+address+email+phone"
            },
            {
              "name": "response_type",
              "value": "code"
            },
            {
              "name": "client_id",
              "value": "${MY_APP}"
            }
          ],
        }
    }

Oddly enough, I never saw the POST request in the browser, but I see it in the SSL Debug. Not sure if it matters, but I see BasicAuth and the client-id/secret
SSL Debug Data

000: 14 00 00 0C 8C 64 A9 39   89 5D 26 3B 45 B1 28 F5  .....d.9.]&;E.(.
2020-01-25 06:29:41,817 DEBUG [io.net.han.ssl.SslHandler] (vert.x-eventloop-thread-26) [id: 0xbc450dd4, L:/192.168.0.121:65306 - R:XXXX.XXX.com/XXX.YY.XXX.XXX:443] HANDSHAKEN: TLS_RSA_WITH_AES_256_GCM_SHA384
Padded plaintext before ENCRYPTION:  len = 464
0000: .............................POST /as/token.o
0010: .............................auth2 HTTP/1.1..
0020: .............................Authorization: B
0030: .............................asic ${ REMOVED}
0040: .............................${REMOVED      }
0050: .............................${REMOVED      }
0060: .............................${ REMOVED  }..C
0070: .............................ontent-Type: app
0080: .............................lication/x-www-f
0090: .............................orm-urlencoded..
00A0: .............................Accept: applicat
00B0: .............................ion/json,applica
00C0: .............................tion/x-www-form-
00D0: .............................urlencoded;q=0.9
00E0: ...............................Content-Length
00F0: .............................: 191..host: ssa
0100: .............................${REMOVED   }...
0110: ..............................code=${REMOVED}
0120: .............................${REMOVED      }
0130: .............................${REMOVED    }&r
0140: .............................edirect_uri=http
0150: .............................%3A%2F%2Flocalho
0160: .............................st%3A8080%2Fsecr
0170: .............................e&client_id=arti
0180: .............................${REMOVED }&gran
0190: .............................t_type=authoriza
01A0: .............................tion_code&client
01B0: ............................._secret=${REMOD}
01C0: .............................${REMOVED      }

I'll continue to review my configuration and logs for additional insights or mistakes on my side (which is very possible)

@jessegduke Thank you, we had an Okta user confirming the same issue was fixed for him. May be the old Qurakus is still loaded

Possible. To test that, I just removed my ~.m2/repository and rebuilt both Quarkus MASTER and application and still experienced the same HTTP/1.1 401 . FWIW: This is not critical for me, I just wanted to port an existing Spring Boot application that I use for demonstration purposes for the value of OCP, K8S, and microservice design. No urgency on my part at all. I just appreciate what I see happening in Quarkus

@jessegduke thanks, it is important indeed that Quarkus interoperates will all the certified OIDC servers, including Ping Identity,
My understanding all is happening here in Vertx:
https://github.com/vert-x3/vertx-auth/blob/e8634fe31ba0ef0c565b097d0cb5d9141a9cd4e5/vertx-auth-oauth2/src/main/java/io/vertx/ext/auth/oauth2/impl/flow/AbstractOAuth2Flow.java

Can you please try to put a breakpoint there ?
Thanks

For what it is worth I just built my application with 1.2.0.Final, added

<dependency>
        <artifactId>vertx-auth-oauth2</artifactId>
        <groupId>io.vertx</groupId>
        <version>3.8.5</version>
</dependency> 

to my pom.xml file, started Quarkus, accessed the application, I was redirected to Okta, and after successful authentication I was able to access my application. Without the dependency my app goes into a redirect loop.

Here is my config:

quarkus.oidc.auth-server-url=https://xxxxx.okta.com
quarkus.oidc.client-id=XXXXXXXXXXXXXXXXXXX
quarkus.oidc.credentials.secret=XXXXXXXXXXXXXXXXXX
quarkus.oidc.authentication.scopes=openid,profile,groups
quarkus.oidc.authentication.redirect-path=/
quarkus.oidc.application-type=web-app
quarkus.oidc.enabled=true

quarkus.http.auth.permission.authenticated1.paths=/*
quarkus.http.auth.permission.authenticated1.methods=GET,POST,PUT,DELETE
quarkus.http.auth.permission.authenticated1.policy=authenticated

Hi @aaronanderson, thanks, I've opened #6802 to take care of the redirect loops. @jessegduke, FYI.

OK, then, I'm still keeping my fingers crossed that @jessegduke will eventually confirm that it works with Ping Identity too :-)

@FroMage Hi Stephane, re your earlier query about connecting with the multiple well known social providers, please see a side note from @jessegduke in the description about connecting to Google. You can combine several configurations like that one and select dynamically using a multi-tenancy feature, see the OIDC multitenancy test, Pedro will hopefully have time to document it once he gets back. For example, you can have a Qute-generated page with several login options, clicking on either of them would send a provider qualifier and the multi-tenant provider would select the right config. One would likely still be better by letting the single OIDC provider manage all of it, but it should work now...

Thanks @aaronanderson and @sberyozkin ... I will give these configs/versions a try later (I hope later this week at the latest).

@aaronanderson and @sberyozkin . I decided to be late to work a few mins and test it out, and THAT DID IT. Moving to 1.2.0.Final fixed it - Seems to work with PingFederate now! Thanks, everyone.

@jessegduke thanks, hopefully it did not take much time and you were not too late for work :-). I'll resolve for 1.3.0. @aaronanderson thanks for your support on this issue too

Thanks @sberyozkin

Was this page helpful?
0 / 5 - 0 ratings