Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Dynamic (or forever) expiration time? - Single Sign On approaches. #71

Closed
miyagawa opened this Issue · 22 comments

10 participants

@miyagawa

Correct me if i get the code mistaken, but it seems doorkeeper has expires_in for the OAuth token as a required column, which means any token has an explicit expiration time, and that could only be specified as a static value via the configuration file.

I know that doorkeeper supports refresh token mechanism, but it is a bit painful to write the logic to refresh tokens in OAuth2 client applications such as mobile apps, and based on my interviews with some developer friends, their life could be much easier if the site passes a token that never expires (until the user has revoked).

Sites like Foursquare, GitHub and Yammer do this, and Facebook allows this when you request an authorization with offline_access (which they will deprecate but that's another story).

The current workaround may be to set a really long expiration time such as 10 years, but that causes another problem since the expiration time is shared between the token flow and code flow - i want a shorter expiration time for token because of the security.

So - I want some kind of dynamic configuration to override the determination of the expiration time in run time (like Facebook's offline_access), which then supports 0 or some kind of special value to mean "never expires". What do you think?

@mattgreen

I'm looking for this feature as well.

Doorkeeper devs, if I submitted a pull for this feature with accompanying specs, would you be open to accepting it?

@felipeelias
Owner

@mattgreen we'll be open to accept it :-)

@miyagawa we thought about it, we just didn't implement because of the specs, but it is very requested feature.

I think some dynamic configuration would help. Maybe we should use the same idea of #70? This way you could configure the expiration depending on resource owner/client/scope or others.

Thoughts?

@mattgreen

What if we used a config block, much like resource_owner_authenticator? I can imagine the block accepting two parameters: the resource_owner, as well as the token. Additionally, perhaps it could be used to signal that a token should not be issued; say, by raising a specific exception.

By doing this, you could enforce odd requirements, such as "only one access token issued per x days", etc. It's tricky to know where to draw the line in terms of a sane level of configuration.

@felipeelias
Owner

@mattgreen not sure if this would be the exact place to handle the case for "only one access token issued per x days". I like the idea though.

This will be something like a decision of the expiration time based on some parameters, few use cases:

  • If the request has a specific scope, like offline_access, then return nil as expiration time
  • If the client (or resource owner) has an specific attribute or permission (maybe "admin" client)

So we could use a block like:

access_token_expiration do |client, resource_owner, request|
  if request.scopes.include? :offline_access
    nil
  elsif client.admin?
    10.hours
  else
    2.hours
  end
end

# and then, during token creation
Doorkeeper.configuration.access_token_expiration.call(client, resource_owner, self)
# => 2.hours (for example

Not sure if this would be the best way to handle this, I'd love to hear some thoughs.

@felipeelias
Owner

Just started an experimental configuration for this on this branch: https://github.com/applicake/doorkeeper/compare/dynamic-expiration

@felipeelias felipeelias was assigned
@mmzoo

I like the block approach! I need to configure the expiration time per consumer (infinite expiry for iphone and 2 hours for web), so this approach would solve my problem, too.

@bryanrite

:+1: for the block approach as well.

@luxerama

This is exactly what I am looking for. Is there anything I can do to help?

@ksheurs

was something like this ever implemented?

@niftyn8

So - I want some kind of dynamic configuration to override the determination of the expiration time in run time (like Facebook's offline_access), which then supports 0 or some kind of special value to mean "never expires". What do you think?

@miyagawa Please do not implement a feature like that with no expiration time. Expiring access tokens is a security feature... Not expiring them opens applications to attack. In mobile apps most of all since the access token is actually user accessible and not passed on back channels between two web servers.

Instead you may want to investigate implementing an offline_access feature via a scope. If a client has that scope it should be able to request a new access token with a refresh token even if the user doesn't have a current session.

You may also want to look at the OpenIDConnect Spec and in particular the section on Offline Access.

@miyagawa
@tute
Owner

Default expiration time can be configured in https://github.com/doorkeeper-gem/doorkeeper/blob/84f809aa98fcde3272d45e0381ebb6829df8f8f7/lib/doorkeeper/config.rb#L176. Accepts values in seconds (default is two hours), so 10 years is around 315.360.000 seconds. Thanks for your input!

@tute tute closed this
@mmzoo

Can we open this issue again?

I need different expiration times for different resource owners or applications, say 10 hours for admins and 2 hours for normal users, just as @felipeelias demonstrated.

Please do not implement a feature like that with no expiration time.

This issue is, in my eyes, not primarily concerned with unlimited expiration time. I agree that that would not be good.

However, the block approach would allow for different expiration times for different users and/or applications rather than one for all. I can e.g. imagine the user checking a checkbox at login saying "This is a public computer, my token should only be valid 10 minutes just for this session" or something like that.

Or, say, 1 hour on mobile devices but 10 hours on desktop clients.

@niftyn8

@mmzoo Are you trying to use access tokens for session management? I urge you to please not.

Since access tokens should only be used to determine if a client application has authorization to act on the user's behalf it shouldn't matter how short the access tokens live. The client application can (and should) refresh their access token as needed to continue their authorization to user data (if the provider can determine the user still gives their consent).

Whether or not the user has a session is up to the client to determine. That maybe using by using an access token to get some data back from the provider or another service. But the token itself shouldn't be an indicator of having a session. IMHO, anything more than about 10 minutes is an irresponsible amount of time to let an access token be considered valid. According to section 5 of the OAuth 2 spec, access tokens should live < 1 hr.

Issue short-lived bearer tokens:  Token servers SHOULD issue
      short-lived (one hour or less) bearer tokens, particularly when
      issuing tokens to clients that run within a web browser or other
      environments where information leakage may occur.  Using
      short-lived bearer tokens can reduce the impact of them being
      leaked.

I need different expiration times for different resource owners or applications, say 10 hours for admins and 2 hours for normal users

Can you talk some about your specific use case? There's probably another way to tackle the same thing that doesn't not involve leaving user data open to unauthorized access.

@mmzoo

@niftyn8 I appreciate your commitment very much, thank you :) Yes, I seek to implement a single-sign-on architecture with "server side session authentication" and found that, if I were to build that from scratch, it would look like OAuth.

My use case

I have the client applications alpha.dev and beta.dev etc. and the doorkeeper server doorkeeper.dev.

My goals are
  • The end user can seamlessly switch between alpha and beta without having to log in twice
  • The end user can logout of all client applications with one click
  • The end user has an overview of all ongoing sessions, just like Github has in your account settings, and can remotely invalidate them
A few conditions/assumptions are given
  • I have full control over every application (except, say native mobile client apps where secrets and traffic can be forcibly extracted)
  • All traffic is TLS encrypted
  • There is no shared data store, if any application wants to talk to another, it has to be via API requests
  • Every client app is a separate OAuth Doorkeeper consumer, that is, a token of one app cannot be abused for another app
My suggested solution is
  • When logged in in alpha, there is some form of unique token in the local cookie
  • At every request (or, at least rather frequently), I let alpha validate that token against the list of sessions at doorkeeper.dev (say, via requests signed with that token to avoid actually sending the token over the wire again and again)
  • With that request to doorkeeper.dev I can also update the latest session activity and retrieve the current set of authorization rules for that user
My thoughts are
  • The Doorkeeper gem basically already provides that architecture
  • Everybody says OAuth access tokens should be short-lived to prevent misuse of leaked tokens (and I agree), but is a cookie not a long-lived shared secret as well? I guess it's not passed around that much though...
  • Heroku solves this via a cross-subdomain cookie, but I don't have the same TLD so I cannot do that
  • Github can maintain the sessions sever-side because there is (AFAIK) just one application, not alpha and beta like in my case

access tokens should only be used to determine if a client application has authorization to act on the user's behalf

This is true. But let's take the simple example of the user changing the password (which only the server is aware of) because the user feels that it might have been stolen. I want to stop the client application from acting on the user's behalf immediately.

The client application can (and should) refresh their access token as needed to continue their authorization to user data

So you want me to ensure some sort of access token rotation by using refresh tokens? I just felt a little awkward sending refresh tokens over the wire basically all the time. But then again, I see your point. If someone steels my cookie it would only be valid for a very short time.

If that's all then I can implement that! :) It would cost me an additional round-trip though in case the access token timed out. I don't feel it's ideal that that could happen at every request, but I could live with it if it's more secure.

Whether or not the user has a session is up to the client to determine

Yes, but in the case of SSO it should be up to the server, not the client.

But the token itself shouldn't be an indicator of having a session

I thought this was a practical thing to do in my use case. Actually, I believe that having some sort of shared token between the client and the server is the only way to implement this. May it be either a rolling token or a long-lived one.

But, to summarize: The major problem you see is that leaking a token gives evil people access for too long time? What if I roll the token every minute using a refresh token? And would it not be even more secure to never send tokens over the wire by just signing the requests instead of sending the actual tokens? I could keep a separate, public session identifier around so that the server knows which access token to validate the signature against...

@niftyn8

@mmzoo Do you have an email I can send a response to? I have some ideas and suggestions from what I've built in the past to handle use cases like auto sign in, auto sign out, handling access tokens when a user changes credentials, knowing about session across applications, etc. I've built and thought about all of it before and would love to discuss with you but hate to fill this issue with our implementation details.

Some brief details:

Option 1: Shared data store
I know there was an assumption that this is not the case, but if it's a possibility and all the clients will be behind a firewall it may make sense... This won't help if a 3rd party app or a mobile app needs to know about the session, but it's an option. I haven't gone with this option in the past for those very reasons so I'm not confident I can think out all the pros and cons but some are apparent (having to be behind the firewall, availability of the data store, etc.)

Option 2: Session state

One generic suggestion would be to have the provider calculate an opaque session_state value that is sent to the client along with the user's information. This session_state would be recalculated every time there's a change to their session's status. The client can hit an endpoint sending the session_state it knows about to the server and the server can respond with whether or not the state is still valid. If it's not valid the client can end the user's session and perform another handshake with the server to determine whether or not the user is currently signed in. How you store it, when you send it to the provider to determine the session status, etc. is kind of out of scope and hard for me to advise you in with out more intimate knowledge of your specific use case and needs... But generically, that's one way to do it. As far as making sure the client app I authorized to that information, you could do something like the client credentials flow.

There's even more ideas in the OpenID Connect Session Management spec. OpenID Connect is primarily an identity layer on top of OAuth 2 but there's also some good ideas in there for how to handle different use cases like yours.

@ksheurs

@niftyn8 @mmzoo would love if you guys could loop me in as well (ksheurs at gmail). i've been looking to build something very similar. thank you!

@mmzoo

@niftyn8 Brilliant. My thoughts were actually progressing into exactly that direction (session state) but you were some steps ahead of me :) I was just creating a sessions table in my doorkeeper app to keep track of those...

You can reach me via sso🎩posteo.ee so we don't have to spam this thread :)

Thank you doorkeeper.

@tute
Owner

This is a great discussion guys, please move on in a Github issue/wiki instead of on private email! :+1:

@tute tute changed the title from Dynamic (or forever) expiration time? to Dynamic (or forever) expiration time? - Single Sign On approaches.
@mmzoo

It turned out that the discussion moved around over here for those interested.

@tute
Owner

Also checkout the projects and talks of @jagthedrummer, he's been doing quite a good job on this (and presenting in conferences). :+1:

@jagthedrummer

Thanks for the mention, @tute! Here's a link to a bunch of resources for a talk I gave at RailsConf about using doorkeeper for SSO.

UPDATED : I forgot to include the link. :facepalm:

http://www.octolabs.com/blogs/octoblog/2014/04/22/service-oriented-authentication-railsconf/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.