How I found OIDC violation in identity provider

From time to time, everyone has to do some dirty work, and this time it was enabling SSO (Single Sign-On) on a legacy, self-hosted Java third-party system. It was just me standing between two enterprise services.

As usual, I read the documentation, created the required configuration on both sides, and deployed the changes. You know the drill.

Everything loaded fine, yet I still couldn’t sign in. Configuring SSO is not rocket science, so I was pretty sure I had done everything correctly.

My reward was an unreadable Java stack trace, which, unsurprisingly, wasn’t helpful at all and customer support was not help at all. Let’s dig in.

First of all, I needed to know exactly what was going on. At the end of the day, it was just a bunch of HTTP requests between two services, so inspecting the requests should have been helpful. I had all the access I needed, but the traffic was encrypted, and I couldn’t just get rid of TLS since I only controlled one side—the service we were integrating with SSO.

At this stage, packet capture is useless because it contains only handshakes and encrypted noise.

TCP        53422 → 443 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 WS=256 SACK_PERM
TCP        443 → 53422 [SYN, ACK] Seq=0 Ack=1 Win=65160 Len=0 MSS=1460 WS=256 SACK_PERM
TCP        53422 → 443 [ACK] Seq=1 Ack=1 Win=502 Len=0
TLSv1.3    Client Hello
TLSv1.3    Server Hello, Change Cipher Spec, Encrypted Extensions
TLSv1.3    Certificate, Certificate Verify, Finished
TLSv1.3    Finished
TLSv1.3    Application Data
TLSv1.3    Application Data
TLSv1.3    Application Data
TLSv1.3    Application Data

Since I have access to the application, I can simply enable TLS key logging and decrypt the traffic. If you’re not familiar with key logging: a key log is a file containing the cryptographic secrets the client uses to derive TLS session keys. With those keys, tools like Wireshark can decrypt TLS just by using the same secrets the application already had in memory. I like this method because I don’t need to configure any proxies or set up TLS termination between the services which is not even possible in some cases.

So how do I enable key logging for a Java process? No idea. But after a couple of Google searches, I happily discovered that there are already tools out there (like extract-tls-secrets) that can attach to a running Java process and dump TLS session keys to a file. Easy peasy. The less I do, the better.

java -javaagent:/tmp/extract-tls-secrets.jar=/tmp/secrets.log -jar my-app.jar

With the key log in hand, I can finally decrypt my PCAP where I see 405 response.

 HTTP     POST /userinfo HTTP/1.1  (application/json)
 HTTP     HTTP/1.1 405 Method Not Allowed  (application/json)

This is definitely fishy, the application definetily need correct response from this endpoint. For context, the UserInfo endpoint in OIDC is the place where the client retrieves the authenticated user’s profile information after obtaining a valid Access Token from the token endpoint. It’s essentially the “tell me who this user is”.

According to OIDC specification:

 The Client sends the UserInfo Request using either HTTP GET or HTTP POST. The Access Token obtained from an OpenID Connect Authentication Request MUST be sent as a Bearer Token, per Section 2 of OAuth 2.0 Bearer Token Usage [RFC6750].

It is RECOMMENDED that the request use the HTTP GET method and the Access Token be sent using the Authorization header field.

The following is a non-normative example of a UserInfo Request:

  GET /userinfo HTTP/1.1
  Host: server.example.com
  Authorization: Bearer SlAV32hkKG

This is actually pretty good for me as I can throw the ball at both sides. According to the sniffed traffic, the application I’m hosting is using POST, even though the protocol recommends GET. At the same time, my SSO provider is violating the spec by not supporting both HTTP methods.

For me, that means opening two tickets, since I don’t have access to either codebase, and then just waiting for someone to fix their side.

It’s always good to find out it wasn’t me who messed up.

*cracks a can of beer*