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

Logout #5

Open
RangeVsRange opened this Issue Mar 22, 2015 · 3 comments

Comments

Projects
None yet
3 participants
@RangeVsRange

RangeVsRange commented Mar 22, 2015

What's the right way to logout the user? Explicitly expire the oidc_id_token cookie?

@SteelPangolin

This comment has been minimized.

Show comment
Hide comment
@SteelPangolin

SteelPangolin Mar 25, 2015

Collaborator

Good question.

Assuming a cooperative browser, yes. This would be appropriate for a logout link. Note that because of the way OpenID works, the user will simply be logged back in next time they visit the site, unless they also log out of the IdP as well (see the "single sign-out problem").

If this isn't desired behavior, you can force the user to go through the IdP again:

  • Modify the cookie validation code to always check for stored credentials under that sub (OpenID subject).
  • Delete a user's credentials from the credentials store when they log out.

Assuming a hostile/defective browser or stolen cookie, that alone won't do it, because a browser or other user agent could ignore cookie expiration and retain the cookie. It'll eventually stop working when the signed expiration time passes, but can be used until then. So we'd need to implement something suitable for a "log me out everywhere" scenario.

Some possibilities to deal with this:

  • Store a counter in the token cookie and in the credential store. Always check the cookie's counter value when validating the cookie. When a user logs out, increment the stored counter. Now cookies generated before the logout are invalid.
  • Every time a new cookie is generated, also generate a unique ID. Store it in the cookie and in the credential store. Always check the cookie's unique ID when validating the cookie. When a user logs out, delete all the stored unique IDs. Now cookies generated before the logout are invalid. This is similar to the counter approach.
    • The user may also selectively invalidate cookies by unique ID if you provide an interface for it. Each unique ID could be stored alongside a browser fingerprint, geolocation, etc. so that the user can see where they're logged in. For an example, see Google's Devices & Activity page.
Collaborator

SteelPangolin commented Mar 25, 2015

Good question.

Assuming a cooperative browser, yes. This would be appropriate for a logout link. Note that because of the way OpenID works, the user will simply be logged back in next time they visit the site, unless they also log out of the IdP as well (see the "single sign-out problem").

If this isn't desired behavior, you can force the user to go through the IdP again:

  • Modify the cookie validation code to always check for stored credentials under that sub (OpenID subject).
  • Delete a user's credentials from the credentials store when they log out.

Assuming a hostile/defective browser or stolen cookie, that alone won't do it, because a browser or other user agent could ignore cookie expiration and retain the cookie. It'll eventually stop working when the signed expiration time passes, but can be used until then. So we'd need to implement something suitable for a "log me out everywhere" scenario.

Some possibilities to deal with this:

  • Store a counter in the token cookie and in the credential store. Always check the cookie's counter value when validating the cookie. When a user logs out, increment the stored counter. Now cookies generated before the logout are invalid.
  • Every time a new cookie is generated, also generate a unique ID. Store it in the cookie and in the credential store. Always check the cookie's unique ID when validating the cookie. When a user logs out, delete all the stored unique IDs. Now cookies generated before the logout are invalid. This is similar to the counter approach.
    • The user may also selectively invalidate cookies by unique ID if you provide an interface for it. Each unique ID could be stored alongside a browser fingerprint, geolocation, etc. so that the user can see where they're logged in. For an example, see Google's Devices & Activity page.
@RangeVsRange

This comment has been minimized.

Show comment
Hide comment
@RangeVsRange

RangeVsRange Mar 25, 2015

Note that because of the way OpenID works, the user will simply be logged back in next time they visit the site

This is a problem with @check. It's okay for some views. However, in my application, I have some views that I want to be available either authenticated or anomymously. If the user is logged in, I want to show them additional information - at the very least an 'account' menu that includes their name. If the user is not logged in, I don't want to annoy them by sending them to Google, just to view this page.

As a developer, the use case for me here is that someone who is logged in and viewing an authenticated page can 'share' the page via Facebook etc., and then anyone who clicks through from Facebook etc. gets the smoothest possible user experience.

Anyway, back to the original question: I was referring to user-initiated logout, not app-initiated logout / premature session expiry. It's a simpler problem, I think. What I'm doing at the moment, and I think you said this approximately correct, is:

response = redirect(url_for('home_page'))
response.set_cookie(OIDC.id_token_cookie_name, expires=0)
return response

For completeness, here's my is_authenticated method, which I'm using in some places instead of @check:

def is_authenticated():
    """
    Is the user authenticated with OpenID Connect?
    """
    if request.cookies.get(OIDC.id_token_cookie_name, None) is None:
        # Otherwise OIDC gets a little confused
        return False
    else:
        return OIDC.authenticate_or_redirect() is None

RangeVsRange commented Mar 25, 2015

Note that because of the way OpenID works, the user will simply be logged back in next time they visit the site

This is a problem with @check. It's okay for some views. However, in my application, I have some views that I want to be available either authenticated or anomymously. If the user is logged in, I want to show them additional information - at the very least an 'account' menu that includes their name. If the user is not logged in, I don't want to annoy them by sending them to Google, just to view this page.

As a developer, the use case for me here is that someone who is logged in and viewing an authenticated page can 'share' the page via Facebook etc., and then anyone who clicks through from Facebook etc. gets the smoothest possible user experience.

Anyway, back to the original question: I was referring to user-initiated logout, not app-initiated logout / premature session expiry. It's a simpler problem, I think. What I'm doing at the moment, and I think you said this approximately correct, is:

response = redirect(url_for('home_page'))
response.set_cookie(OIDC.id_token_cookie_name, expires=0)
return response

For completeness, here's my is_authenticated method, which I'm using in some places instead of @check:

def is_authenticated():
    """
    Is the user authenticated with OpenID Connect?
    """
    if request.cookies.get(OIDC.id_token_cookie_name, None) is None:
        # Otherwise OIDC gets a little confused
        return False
    else:
        return OIDC.authenticate_or_redirect() is None
@puiterwijk

This comment has been minimized.

Show comment
Hide comment
@puiterwijk

puiterwijk May 21, 2016

Owner

I have just added an oidc.logout() function, which will ensure log outs for cooperating browsers by expiring and emptying the cookie.
I will look into adding OpenID Connect Single Logout, and also logout for non-cooperating browsers, in the near future.

Also, with the new version you will also get the ID token if your API does not require authentication.
To indicate this, the check function has been renamed to require_login with a backwards compatibility later for check.

Owner

puiterwijk commented May 21, 2016

I have just added an oidc.logout() function, which will ensure log outs for cooperating browsers by expiring and emptying the cookie.
I will look into adding OpenID Connect Single Logout, and also logout for non-cooperating browsers, in the near future.

Also, with the new version you will also get the ID token if your API does not require authentication.
To indicate this, the check function has been renamed to require_login with a backwards compatibility later for check.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment