Announcement Announcement Module
Collapse
No announcement yet.
using s2-oauth provider for sign-on: one-time grant prompt and returning same token Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • using s2-oauth provider for sign-on: one-time grant prompt and returning same token

    i'm working on a project where we are trying to implement an oauth(2) provider that can be used for sign-on.
    for example, a tonr-ish client might have a button labeled 'sign on using your sparklr-ish account',
    and when you click it, it does the oauth dance:

    (a) prompts you to sign on to sparklr-ish
    (b) prompts you to grant access to sparklr-ish resources
    (c) gets an auth code and exchanges it for an access token
    (d) uses that access token to get a 'user profile' resource with some basic info (first name, last name, username), this basic premise is captured by spring-social

    so far, so good.

    for this sign-on-using-oauth-provider pattern, we might logout of tonr-ish (having not made any attempts to persist the access token), come back the next day and click the 'sign on using your sparklr-ish account'.

    the desired effect is that:

    (e) the user is prompted to sign on to sparklr-ish
    (f) the user is not prompted to grant access again since they already did this previously
    (g) the client gets an auth code and exchanges it for an access token (i think it should be the same access token that was provided the previous day, just so the provider isn't stacking up a new access token in it's database for each of multiple logon events)
    (h) the client gets the 'user profile' resource using the access token and does whatever 'account joining' activities it cares to do in terms of reconciling the tonr-ish login session with the sparklr-ish user-profile.

    so my questions are generally:

    - has anyone successfully accomplished anything like this with s2-oauth?
    - if so, any tips?
    - is there anything about s2-oauth that would make implementing this behavior cumbersome?
    - any other recommendations or thoughts on the topic?

    thanks,
    tony.

  • #2
    I have implemented such as system, and it seems to work OK. It's basically the way that the OpenID Connect standard works (http://openid.net/specs/openid-conne...rd-1_0-07.html). We might even support some form of the standard in Spring Security at some point if they get the spec finished.

    I'm quite interested in your points about token persistence, and I think they would bear some more analysis. If you find that the framework stores more tokens than necessary, then we could work on fixing it.

    Remembering past approvals is not a feature of the framework as it stands, but you may find that normal cookies and servlet sessions have the desired effect anyway. It's not really covered in the spec, and it seems that any practical implementation would be limited by the servlet session expiry anyway (or other persistence if you changed the authentication storage strategy in Spring Security).

    Comment


    • #3
      hi dave,

      appreciate the response.

      i'm familiar with open-id, but not open-id-connect.

      scanning the link you sent, it seems that is exactly what i am after,
      but it also indicates that plain-old-oauth doesn't really cover the authentication use case all by it's lonesome.

      generally, it seems like oauth expects the client to hold on to the token after a single dance,
      but the authentication case leads us to invoke the oauth-dance for each login, so therein lies the rub.

      the desired effect from the provider is that when a client-user initiates the oauth-dance (as it will for each login),
      prior to prompting the client-user for a grant, the provider looks in it's database to see if a token has already been issued to this client-user, if so (and it hasn't expired), the grant prompt should be skipped and the some token should be issued to the client-user (or minimally the existing token-row should be updated with a new token, or deleted prior to adding a new token-row) this will avoid token-row creep with each login event.

      do you think s2-oauth can be 'hooked' in such a way as to gain the desired effect?

      i wouldn't necessarily expect the out-of-the-box jdbc implementations to behave this way,
      but it would be cool if the interfaces supported this behavior with customized implementations.

      if this use-case wasn't on your (and/or ryan's) radar when you designed the interfaces, there may be some modification required at that level. for instance, i think we would need the ability to:
      (a) allow for a way to lookup a token based on client-user, and
      (b) skip the grant prompt

      Comment


      • #4
        I think the point of OpenID Connect is not to extend OAUth2 where it has missing features per se (but there are extensions on the table) - it is really just defining a set of standard endpoints and scopes for exchanging user profile data using the existing protocol. So I don't necessarily agree with your statement that authentication can't be handled with vanilla OAuth2 (which is what you get out of the box with Spring Security OAuth at the moment).

        I don't think the token service APIs need to change, and like I said I already have a system working. It should be in production next week (light load to start with). Skipping the grant prompt is a decision the client app has to take, so maybe that's the link with the UserDetails you were asking about - if you look at the OpenID support in Spring Security you can see that there is a UserDetails component, and it is up to the client app to decide how much data to store and for how long. Recent snapshots will skip the grant prompt automatically if all the data is in the authorization code request, but even in older releases all you need to do is provide a custom user approval page.

        Comment


        • #5
          i didn't mean that authentication couldn't be handled with vanilla oauth2, i meant that the spec doesn't explicitly cover that use case
          (and that's where something like openid-connect can add value w.r.t. standardization).

          w.r.t. your app, not sure how much you can disclose, but do you control both the client and provider, or just one of the two?

          in my particular case, we currently have spring-social at the client and s2-oauth (m4) at the provider.

          i'm sure it's (at least partially) me, but i'm confused about the client handling the skipping the grant prompt.

          trying to follow the flow with tonr/sparklr using the network panel in chrome developer tools, i witness something like:

          (1) /tonr/sparklr/photos->302->/sparklr/oauth/user/authorize->302->/sparklr/login.jsp

          (2) /sparklr/login.do->302->/sparklr/oauth/user/authorize->302->/sparklr/oauth/confirm_access

          so it never returns control to /tonr before it 302's to the confirm_access.

          how would a client (e.g. spring-social in my case) be responsible for the grant prompt decision using this flow?

          Comment


          • #6
            On the server you can set the userApprovalPage to forward (instead of redirect) to a controller that handles the authorization itself, instead of prompting the user. That would always work. Since last week if you control the client and the server you can also get the client to send oauth_user_approval=true along with the other authorization request stuff. If Spring Social can't be modified to do that I will be surprised, but I haven't tried it myself (my apps all use Spring Security OAuth on the clients).

            Comment


            • #7
              cool! learn something new every day...

              can you confirm my understanding:

              (1) set provider-side to use forward (v redirect) for userApprovalPage:
              - so this gives me a provider side controller hook to determine whether to display the consent page
              (still provider side decision v client side yes?)

              (2) have client pass oauth_user_approval=true to have provider skip consent page
              - the client would need enough information about the user to know whether to set this to true or false,
              but in my particular case, the client doesn't know who the user is until it can successfully login to the provider,
              obtain an access token and call back in to the oauth provider for some user-profile information. so it seems like this hook won't work for me, but i can't help feeling i'm missing something...

              thx,
              tony.

              Comment


              • #8
                Your replay sounds about right. I haven't tried authentication with automatic approval, nor have I tried it with Spring Social, so I can't really comment in detail on that right now. But I don't see why you need to know who the client is to send the oauth_user_approval=true flag (you just want to be sure that your user really does approve - presumably if the scope is narrowed to retrieving user info for the authentication it's easy to argue that any user would approve).

                Comment


                • #9
                  ok, i see what you are saying about potentially unconditionally having the client flag the provider to skip the consent prompt.

                  i was thinking that we would want to obtain consent the first time through, in which case, if the client knew the user, it could determine if the user had been through the handshake once and conditionally pass the flag.

                  did you say you were working on an application that uses s2-oauth for authentication purposes?

                  if so, how does your version of this pattern manifest? (e.g. does it unconditionally prompt for consent each time the same user logs in?)

                  my other concern, which is perhaps tangential to the consent prompt, is that the provider not unconditionally construct a new token each time the client-user logs in, but rather checks the provider side token store to see if this client-user has already been granted a token and if so, just returns the existing one (or the other variants i itemized that result in only one token per client-user in the provider side token store).

                  it looks like that can be accommodated though cause the signature of TokenStore.getAccessToken() takes an OAuth2Authentication reference which includes an Authentication reference which (i think) should have the actual user info in it (not just the client), so an implementation of TokenStore should be able to enforce the behavior i'm describing. does this sound correct?

                  Comment


                  • #10
                    Originally posted by tony_k View Post
                    if so, how does your version of this pattern manifest? (e.g. does it unconditionally prompt for consent each time the same user logs in?)
                    It prompts a lot. At the moment we rely on cookies to store enough state to prevent the confirmation becoming annoying, but I can see where you are going with the token persistence below (hence I added some features). If you could try it out you might find the right direction to start slicing to provide that feature better (there is also a relevant JIRA ticket https://jira.springsource.org/browse/SECOAUTH-129).

                    my other concern, which is perhaps tangential to the consent prompt, is that the provider not unconditionally construct a new token each time the client-user logs in, but rather checks the provider side token store to see if this client-user has already been granted a token and if so, just returns the existing one (or the other variants i itemized that result in only one token per client-user in the provider side token store).

                    it looks like that can be accommodated though cause the signature of TokenStore.getAccessToken() takes an OAuth2Authentication reference which includes an Authentication reference which (i think) should have the actual user info in it (not just the client), so an implementation of TokenStore should be able to enforce the behavior i'm describing. does this sound correct?
                    Yes. And I think the existing *TokenServices now support this out of the box (since yesterday) - you might have to provide an OAuth2AuthenticationKeyGenerator, but the default is probably fine.

                    Comment


                    • #11
                      Full disclosure... I work with Tony.

                      If I may contribute to the discussion...

                      Looking at tonr's integration with sparklr as an example, one would see that tonr does not support anything such as "sign in using sparklr." There is the option to connect to sparkl only after the user has signed in to tonr using a local tonr account. In a case such as this, the client application (tonr) can store the token that the user approved and the provider issued. On subsequent user visits to tonr, tonr can continue to communicate with sparklr using this token that it has stored in association to that user/provider tuple. One notable thing here is that the OAuth dance occurs ONLY once and ONLY one token is issued.

                      Contrast this, however to the "sign in with sparklr" use case that Tony is inquiring about. tonr does not "know" who the user is yet (or else tonr would not be asking the user to sign in, right?) Even if tonr has retained a token for the user/provider tuple in question, that token is of no consequence at this point. Authentication is carried out by a REPETITION of the OAuth dance.

                      The problem is that SSOAuth's out-of-the-box components do not feature any convenient mechanism whereby the provider can acknowledge during the dance that a token has ALREADY been approved and issued for the user/client tuple. Therefore, every repetition of the OAuth dance ultimately results in a new token. Issuing a new token- of course- would require the user to approve it.

                      Besides cultivating a poor user experience, this also results in an completely unnecessary accumulation of tokens on the provider side that have been issued to the same user/client tuple. If a user has signed into tonr via an OAuth dance with sparklr 100 times, then 100 tokens have been issued. Not only is it unnecessary, but it also diminishes the user experience of a "revoke token" use case. If a user wishes to revoke tonr's access, they'd have a hundred tokens to revoke.

                      To streamline this, the provider (auth server) should ideally recognize this condition and bypass the access confirmation page. (Heavy hitters like Facebook do this.) I don't feel that hacking on the controller that backs the access confirmation is an ideal means to implement this optimization. If SSOAuth is implementing the "dance," then SSOAuth, IMO, ought to account for this.

                      And this says nothing of the AuthorizationServerTokenServices interface not supporting operations as fundamental as "find tokens by user id" (necessary to support the revoke token use case) or "find token by user id and client id" (necessary to acknowledge cases where a suitable token has ALREADY been issued to support the use case described above).

                      Again... just as a sanity check, if the OAuth dance is done repeatedly with Facebook with the same user/client combination, not only is the user not pestered repeatedly to approve a token, but the existing, approved token is also re-issued. This seems like reasonable behavior, and I would humbly submit that this should be "sensible default" behavior for SSOAuth.

                      Comment


                      • #12
                        Originally posted by krancour View Post
                        To streamline this, the provider (auth server) should ideally recognize this condition and bypass the access confirmation page. (Heavy hitters like Facebook do this.) I don't feel that hacking on the controller that backs the access confirmation is an ideal means to implement this optimization. If SSOAuth is implementing the "dance," then SSOAuth, IMO, ought to account for this.
                        I think part of the confusion comes from the way you describe the use case - I don't see it as bypassing the user approval page (which is a possible customization we discussed above), but as re-using existing tokens, which (as you point out) seems like a sensible idea as a default.

                        Have you looked at the code recently? We made some changes last week that allow the token service implementations we provide to re-use existing tokens. I think your use case should work out of the box, but I'm happy to look closer if you can provide test cases, or specific implementation details. If there are problems with the implementation we have you can always contribute code to fix it.

                        And this says nothing of the AuthorizationServerTokenServices interface not supporting operations as fundamental as "find tokens by user id" (necessary to support the revoke token use case) or "find token by user id and client id" (necessary to acknowledge cases where a suitable token has ALREADY been issued to support the use case described above).
                        True, but that's a different use case (and not covered by the spec). There is a JIRA ticket open for revocation and token management, so feel free to contribute ideas and code there.

                        Again... just as a sanity check, if the OAuth dance is done repeatedly with Facebook with the same user/client combination, not only is the user not pestered repeatedly to approve a token, but the existing, approved token is also re-issued. This seems like reasonable behavior, and I would humbly submit that this should be "sensible default" behavior for SSOAuth.
                        As I hope it is. If not contributions are gratefully accepted. Thanks for all your analysis.

                        Comment


                        • #13
                          Thanks for the reply, Dave. I'll give M5 a closer look. Admittedly, many of my comments are based on M4.

                          I don't see it as bypassing the user approval page (which is a possible customization we discussed above), but as re-using existing tokens, which (as you point out) seems like a sensible idea as a default.
                          As for the notion of "bypassing" the access confirmation page- that was actually a poor word choice on my part.

                          As I see it, access confirmation is a step in the dance that needs to occur when a NEW token is issued. But if existing tokens are reused/reissued, then the user has already given their approval, and there logically (I think) would be no approve/reject step in that case. I think we might be on the same page, but just articulating it differently.

                          I guess what I'm not entirely clear on yet is this... it all comes back to the AuthorizationServerTokenServices that I previously mentioned. Without methods on that interface to find a token by user id / client id pair, I have difficulty understanding how the "bypass" optimization discussed above could actually be implemented. Obviously, we could extend that interface and use it in our own code, but SSOAuth code (e.g. components that execute the dance- filters in the SS filter chain, etc.) are obviously written against the AuthorizationServerTokenServices interface and it's not immediately obvious to me where/how to customize those components to use our extended interface to locate an existing token and decide to "bypass" access confirmation.

                          I'm sure a deeper dive into the SSOAuth code than we've already gone will be enlightening, but I feel as if anything we do to get this working is going to be a bit of a hack. We'd have to abandon the custom config namespace and start using traditional bean definitions in order to implement the modifications we need. That requires a bit of a feat in terms of reverse engineering the bean definitions that your custom config namespace creates behind the scenes. Bottom line is I feel like this is all totally doable for us... but it would feel like a total hack and wouldn't be easily repeatable on our future projects. Rather than hack, I think we'd much rather work with you to see if there are any better options.

                          Can you send a link to the JIRA issue you mentioned? I think the modifications to AuthorizationServerTokenServices to support a revoke token use case overlap pretty significantly with what we're discussing above.

                          Comment


                          • #14
                            Found them... both of these appear relevant:

                            https://jira.springsource.org/browse/SECOAUTH-189

                            https://jira.springsource.org/browse/SECOAUTH-129

                            Comment


                            • #15
                              Glad that we are converging. Some more comments on your analysis:

                              The changes I'm talking about are recent (since M5). And the "re-use tokens" use case is covered by internal implementation details of the AuthorizationServerTokenServices, so its interface hasn't changed (the TokenStore interface did if you want to customize or validate the change). I don't really see token search and revocation by users as the same thing, and while the existing token service implementations will probably be extended to cover those use cases, I see it as a new interface - UserTokenServices, maybe?

                              Comment

                              Working...
                              X