9

I'm implementing OAuth2 authorization/resource server based on DotNetOpenAuth. My server is going to issue access tokens with very long life time. These tokens are going to be used from iOS devices. The flow, the way I see it, is like this, 1) a user is asked to enter their username/password on iOS device 2) an access token is requested with grant type of Resource Owner Password credentials 3) token is granted and stored on the iOS device for future use.

Now from time to time users get disabled. I would like to revoke the token at the same time. How do I do this? I suspect that I need to use ICryptoKeyStore.RemoveKey method for that, but not sure how to find which key to remove.

Note 1: in future the server will be used by third party web applications.

Note 2: the requirement to have grant type of Resource Owner Password credentials stems from the fact that it was decided that implementing browser redirection on iOS device is not worth the time.

Update 1 Some excavations in the source code suggest that DotNetOpenAuth does not support this ability to force token expiration out of the box. Moreover in the standard implementation lifetime of the token is not even checked. As far as I can see the calss is responsible for this is StandardAccessTokenAnalyzer and it ignores the Lifetime and UtcCreationDate properties. Also it does not seem that the standard ResourceServer class has any database access coded, the token validity checked by the token content only, so it seems that if I need to add ability to expire the tokens I need to wire up the ResourseServer to database myself. Am I missing something?

Update 2 I think I found the answer here: https://groups.google.com/forum/#!topic/dotnetopenid/aLabu1ujkt4 It's not what I was hoping for and I still have a few unclarities. For example, Andrew wrote:

Your custom class then could take an access token, then use a private HTTP request to the authorization server to verify the continued validity of the token.

It is unclear how this verification can happen, given that AccessToken does not include Authorization Id. This can make finding the target Authorization record difficult. In theory we can try to look it up by combination of client, user and issue time, but as far as I can see there are no guarantee that these will be unique.

Andrew Savinykh
  • 25,351
  • 17
  • 103
  • 158

1 Answers1

8

Now from time to time users get disabled. I would like to revoke the token at the same time. How do I do this? I suspect that I need to use ICryptoKeyStore.RemoveKey method for that, but not sure how to find which key to remove.

You revoke tokens by revoking the authorization behind the token. This typically means you delete an entry in the authorizations table of your database. The effect this must have is that your implementation of IAuthorizationServerHost.IsAuthorizationValid will return false for this authorization.

This doesn't immediately revoke access tokens, but it blocks the client from refreshing expired access tokens. So as long as your access tokens have a reasonably short lifetime (an hour or less) then a user's disabled account means that all client access will terminate within an hour.

Note 2: the requirement to have grant type of Resource Owner Password credentials stems from the fact that it was decided that implementing browser redirection on iOS device is not worth the time.

It's your app. But I urge everyone to use the proper browser redirect flow. The user is likely already logged into your server on the device's browser so they can possibly avoid entering their credentials altogether this way, improving your conversion rates. Users are also more likely to trust the browser asking for their credentials than a device app. At least I hope so.

By the way, the resource owner password grant type is likely to not be supported for non-authenticating clients (TBD), which installed device apps will typically be. So you may be forced into using a different grant type.

Update 1 Some excavations in the source code suggest that DotNetOpenAuth does not support this ability to force token expiration out of the box. Moreover in the standard implementation lifetime of the token is not even checked. As far as I can see the calss is responsible for this is StandardAccessTokenAnalyzer and it ignores the Lifetime and UtcCreationDate properties.

DotNetOpenAuth does check for and reject expired access tokens. It's just not in that class. It's checked in the code that deserializes access tokens.

Also it does not seem that the standard ResourceServer class has any database access coded, the token validity checked by the token content only, so it seems that if I need to add ability to expire the tokens I need to wire up the ResourseServer to database myself. Am I missing something?

You are correct that the ResourceServer class does not require any database access, because access tokens are assumed valid for their entire lifetime (they're non-revokable by default). This is why short access token lifetimes are recommended. This isn't as far-out as you might think. For example, ASP.NET forms authentication, which you very likely use already, is based on the same pattern: authenticate the user once, hit the database for a credential check, then issue an encrypted and signed HTTP cookie to the user agent. From that point on, the database isn't hit on every incoming HTTP request -- the cookie signature is validated and then assumed to be valid until the cookie expires. Same principle. Except that in the HTTP cookie case, there's a sliding timeout, so that as long as the user remains active on the site, they never have to reauthenticate. With OAuth 2 access tokens, they expire regardless of how actively they're used, forcing a token refresh which can then be rejected to lock out access.

It is unclear how this verification can happen, given that AccessToken does not include Authorization Id. This can make finding the target Authorization record difficult. In theory we can try to look it up by combination of client, user and issue time, but as far as I can see there are no guarantee that these will be unique.

It's true no ID is included, but the tuple of client-user-issuedate-scope should be unique, as your authorization table should have a unique constraint on it since having duplicates wouldn't make sense. Besides, if they weren't unique, just the presence of any record with that tuple suggests the authorization is valid.

Hope this helps.

Andrew Arnott
  • 80,040
  • 26
  • 132
  • 171
  • Andrew, thank you for taking your time to answer. I'm still working on this problem and I'll absorb your answer very soon and reply properly. Now I'd just like to comment on *DotNetOpenAuth does check for and reject expired access tokens. It's just not in that class. It's checked in the code that deserializes access tokens.* Of course you are right, I've already discovered this =) Didn't know this at the time of writing, sorry. – Andrew Savinykh May 02 '12 at 01:40
  • 1
    *The user is likely already logged into your server on the device's browser* At the moment there is no web application to log-in to. So this is not so. Of course I could do a login page just for the sake of supporting the flow, but since I'm not the one programming the iOS devices the shared decision was to use the ROPC flow. One of the reasons why was that we do not want application to flick to Safari and then back to the app every time a token needs to be renewed. That will proved suboptimal user experience. – Andrew Savinykh May 02 '12 at 08:23
  • 1
    *By the way, the resource owner password grant type is likely to not be supported for non-authenticating clients (TBD)*. This is very interesting bit of information. Could you please expand? I think that what you are saying is illogical, because a native application simply *cannot* guaranty confidential client password safety, so it HAS to be a public client. And since browser interaction in a native application is cumbersome and difficult, then the only viable grant type is ROPC. Making this unavailable as part of the spec seems to be counter-productive. – Andrew Savinykh May 02 '12 at 08:26
  • Andrew, thank you again for time you took to look at and reply to my question. I have a few more above in comments and on SO tagged appropriately. You are helping a lot! – Andrew Savinykh May 02 '12 at 08:28
  • *One of the reasons why was that we do not want application to flick to Safari and then back to the app every time a token needs to be renewed. That will proved suboptimal user experience* This isn't the case. If you use the authorization code flow when you switch to Safari, you'll get a refresh token that will allow you to renew the token yourself from within the app *without switching back to Safari*. – Andrew Arnott May 03 '12 at 14:01
  • Native apps actually *can* keep a secret -- just not one that is shared with other installations. Namely, an anonymous client makes a call to the authorization server saying "please issue me a client id and secret" and then the client tucks the answer away in its private storage. Now the client is an authenticating client. The secret can be "stolen" by the person in control of the device, but that's not actually a security compromise -- that's just hacking. What native apps *can't* do is *ship* with a preloaded secret, because then the hacker can discover *someone else's* client secret. – Andrew Arnott May 03 '12 at 14:04
  • As for whether a native client can use the browser interaction without being cumbersome, I'd argue that indeed they can certainly use browser interaction and make it fairly elegant. I've seen it done many times. Hosting the browser is one option, which makes it virtually transparent to the user. Personally I prefer switching to the real browser so that cookies and such are still there, and the user has that much more reason to trust that they're not exposing their password to an untrusted 3rd party. – Andrew Arnott May 03 '12 at 14:05
  • But ultimately the reason that dotnetopenauth *may* withdraw support for non-authenticating clients' use of the password credential grant type is simply that the authorization server will have a harder time displaying such a grant to the user in their account info page such that the user can revoke it at a later time should they choose to do so. – Andrew Arnott May 03 '12 at 14:06
  • I should clarify an earlier comment I made: the auth code grant type is only allowed for authenticating clients. So as long as you are using non-authenticating clients, you're restricted to the implicit grant type and yes, as you say that would require access token renewal by repeating the flow with the user in the browser. – Andrew Arnott May 03 '12 at 14:43
  • Andrew, *the auth code grant type is only allowed for authenticating clients* could you please tell me what part of the AOuth2 spec mandates it? I can't seem to find where this restriction is imposed, can you give me the section number please? – Andrew Savinykh May 03 '12 at 22:48
  • *an anonymous client makes a call to the authorization server saying "please issue me a client id and secret" and then the client tucks the answer away in its private storage* But this would mean that every copy of application will have it's own client Id! Is this the way it is supposed to work? – Andrew Savinykh May 03 '12 at 22:54
  • @zespri In looking through the spec, it appears it has changed in more recent drafts to [allow non-confidential clients to use authorization_code grants](http://tools.ietf.org/id/draft-ietf-oauth-v2-26.html#rfc.section.4.1.3). Very interesting. [#141](https://github.com/AArnott/dotnetopenid/issues/141) now tracks this issue. – Andrew Arnott May 08 '12 at 00:20
  • Yes, each installed client certainly may obtain its own client_id and secret. This way each client installation is individually trackable. For example, the user can revoke access to a particular installation which the user knows was compromised (stolen laptop, while desktop computer is still in possession, for example). – Andrew Arnott May 08 '12 at 00:22
  • *The user can revoke access to a particular installation which the user knows was compromised*, to think about, this is great =) It turned out that this was one of the problems the business wanted to solve. So at the moment I settled on doing per device client registration and using Authorization Code grant as it seems that your library support this scenario the best. For the iOS device there is a library https://github.com/nxtbgthng/OAuth2Client that is supposed to support Authorization Code grant flow, so we will try that out. And if it does not work out we can switch back to user creds. – Andrew Savinykh May 10 '12 at 19:45
  • Andrew, thank you for opening the issue and for your continued support. It seems that thanks to you I have now a clear way forward. – Andrew Savinykh May 10 '12 at 19:47