OIDC Front Channel Logout – The Missing Manual

Photo of a sloth hanging from a tree in a zoo reaching for a banana that a woman is handing it. It looks like the painting The Creation of Adam by Michelangelo.

Oh my god, this took me like three weeks of asking incredibly dumb questions in retrospect and bumping into furniture, fighting with kubernetes and just generally not understanding what the specification was talking about.

This article is going to talk about the Front Channel Logout of OIDC 1.0, and you can find its specification here:

Final: OpenID Connect Front-Channel Logout 1.0
OpenID Connect Front-Channel Logout 1.0

OIDC, for those not in the know, is an identity layer on top of OAuth 2.1. Basically it’s a way for some application to use a different application’s user account to authenticate users. To put it in non-technical words: whenever you see any of the “log in with Facebook,” or “log in with Apple” buttons, you’re dealing with OAuth 2.1, and most probably OIDC.

💡
OIDC stands for OpenID Connect. OAuth means Open Authorization.
💡
By the way, difference between authentication and authorization is important here: authentication answers the question of “do I know who you are?” Authorization on the other hand answers the question: “now that I know who you are, should you be doing that?”

As with every system that deals with user accounts, there needs to be a way to log in, and to log out. Let’s talk about those:

Logging in

Or why does Facbook even let you use your account there?

Login happens with the “login with whatever” button. I’m going to use Facebook as an example.

Once you click the Log in button on some weird website that says ”Log in with Facebook,” the first thing that happens is you’re taken to Facebook.

There you’re asked to log in, and once you do, Facebook will tell you “hey this weird website wants to use your profile data and is requesting the following information about you, do you trust this site, and are you okay with this login request?” at which point you usually select “yep, carry on.”

Facebook will then redirect you back to the weird website’s domain to a specific page with a bunch of query arguments (the bits in the address that look like ?code=2kl23m4l2m4&somethingelse=lkmlmcd88). Weird website takes most of them, then asks Facebook behind the scenes, server to server, invisible to you “hey I got this code, uh... can I get an authorization token?”

Facebook will say: “yeah I remember, they just logged in, I gave them the code, so here you go, a token!”

And then you’re logged in! You have your own user account on weird website which is a limited copy of the account you have on Facebook, usually just your username and email, but you didn’t need to set a password, because you’re not using a password! You’re using the fact that you have an account on Facebook, and Facebook said it’s okay to have some of your data because you told Facebook that it’s okay to have some data.

Now not all applications can do this just willy-nilly. The developers at weird website actually had to create a new developer application within Facbook, where they said “hey, we want to use Facebook as an authentication provider, here are the details of our website: domain, contact details, etc,” and Facebook gave them a client secret. This is basically just a bunch of settings on an administration dashboard you get when you tell Facebook you’re an application developer.

Without the ID of the application that Facebook knows about, and the correct client secret that Facebook generated for the application, weird website can not use Facebook as a source for users, because the login action would encounter an error of some kind, usually along the lines of “unknown client” or “misconfigured client.”

This weird application that you log into with your Facebook account is called a Relying Party in the OIDC specifications.

There are more Relying Parties

So now that we know how to log into one of those, and that developers can just register their own applications to Facebook, it follows that there can be any number of applications that you log into with your Facebook account.

If in the course of last year you managed to find, like, 7 different websites that all offer to log into them via your Facebook account, and you’ve done so, Facebook now knows that your user is using these 7 Relying Parties. I’m capitalising this, because in the spec they’re just referred to as RPs.

Another cool thing is if you log into one of them, then visit another one, chances are you’re going to be already logged into it as well, because you have an active session to Facebook, and therefore to the sites you logged into with Facebook.

And now you want to log out. It would be logical if when you logged out of one of them, you logged out of all of them, right?

Logging out

Or how do all the Relying Parties figure out your session has ended?

Broadly there are three (four) ways of logging out. In no particular order:

First: clearing site data

Deleting cookies, clearing site data for the domain of one of the Relying Parties. This works for maybe a single request after that, but because your session is still active on the Facebook side, the next time you reload a page, or navigate to a different page you’re going to be logged in.

The OIDC flow on the site you cleared the cookies will have noticed that your session is no longer valid, but it has a token from the time of logging in, so it exchanged it to a new session. All good.

Second: Relying Party checks periodically

If you sign out of Facebook itself, you destroy the main session, so if and when the Relying Parties (the weird websites) want to exchange the token for a session, the session is no longer there, and they will log themselves out. This depends on the applications, how often they check.

Third: Back channel logout

When you log out of one of the Relying Parties by visiting a logout page, at some point your browser will redirect you to Facebook, and you will also be logged out of there. Because Facebook knows you’re also logged into 6 other Relying Parties, it will send a secret server to server message to all of them to kindly ask them to destroy their own sessions so you’re no longer logged in any more.

Fourth: Front channel logout

I'm a thousand words in (1,117), and just now am I getting to the actual reason for this article.

Front channel logout works much the same as back channel logout, except instead of sending server to server messages, Facebook would render “invisible iframes” to the front channel logout urls of all of the clients.

An “invisible iframe” in this context would mean this:

<iframe src="https://domain.of.relying.par.ty/front_channel_logout?sid=somesessionidhere&iss=https%3A%2F%2Ffacebook.com" style="display: none;" />

It’s an iframe, because that’s what the tag is, the source (src) is the front channel logout url, and it’s invisible because of the display: none;. This translates to a single GET request, which when the Relying Party receives it, it checks the session ID sent along (sid=somesessionidhere) and invalidates its own session and logs you out.

Job done, everyone can go home, thank you for reading!

Except...

Except obviously this isn’t the end of it because why would computing be easy?

While I was working on implementing front channel logout capability on one of the Relying Parties so that it can listen for and accept the request to its own /front_channel_logout URL, I kept bumping into the curios case of the request never arriving at the server.

At that point I wasn’t even sure where the iframes were supposed to be rendered, and if they were rendered on the Relying Party’s logout page as the user is logging out, how would that Relying Party know what all the other Relying Parties are that the user has an active session for? Surely it shouldn’t, because that would be a security and privacy catastrophe! It took a while to figure out that it’s actually the OP’s (OpenID Provider, think: Facebook) logout page that contains these iframes. Excellent!

Except I couldn’t figure out where they were, because the logout took such a short amount of time, that I only caught a split second of the browser going to Facebook’s logout page before being redirected immediately back to the original site I was logging out of.

💡
I wasn’t using Facebook, I was using Keycloak, but you’re probably more familiar with Facebook than Keycloak, so I’ll keep the Facebook example going.

Then I had to figure out what exactly the logout URL was for Facebook (Keycloak), navigate there manually, and check for the iframes, but I couldn’t find any of them. It took an accidental discovery of “active sessions” within Facebook’s dashboard to see that I actually had two sets of different sessions. Because I was using two different browsers, and different browsers can’t share session information with each other, which seems incredibly trivial now, and I did feel pretty dumb for about 5 minutes.

And then once I logged into all Relying Parties, and I navigated to Facebook’s logout URL, I still didn’t see the applications listed, however I did see that the iframes were there by viewing the page source. Turns out Facebook won’t list the applications visibly unless you set their names, which is optional to do. But recommended.

And only then did I pay attention that the page was trying to make the requests to the front channel logout URLs, but they were getting blocked by something called “CSP.” In Firefox this attempt is in the network tab of the developer tools, in Safari it’s just a single message in the console that it refused to load the resource, also because of “CSP.”

CSP is content security policy; basically browsers are doing their best at protecting your data and are only willing to load additional resources, like images, javascript files, other pages, from the same domain that the site you’re visiting from, unless there’s no CSP header, or from domains that are very explicitly allowed because the relevant response headers, that usually only the developers of the application can control, tell the browser.

And just to go full circle, the OIDC Front Channel Logout specification version 1.0 also calls out that this is unreliable due to browser security features in paragraph 4.1. Superb!

And now everyone can go home.

In conclusion

  1. Please do not use OIDC Front Channel Logout.
  2. If you need to use front channel logout, please make sure that your OP (OpenID provider) or IdP (Identity Provider) are allowed to make the requests to all the domains in the iframes, which means setting the Content-Security-Policy: frame-src https://example.com; headers for every single Relying Party there’s an active session for. As a developer of a Relying Party, you usually can not control this.
  3. And even if the embedding page (Facebook / Keycloak) has the Relying Party’s domain in its header with frame-src, the Relying Party either needs to not have a CSP header, or it needs to also declare that Facebook / Keycloak can embed that page in an iframe using the frame-ancestors directive: Content-Security-Policy: frame-ancestors https://keycloak.example.com. As a developer, you can control this.
  4. Please just use back channel logout.
  5. Please!

If you enjoyed this, consider subscribing, or leaving a tip!

Photo by Tatiana Tochilova on Unsplash