openid / AppAuth-Android

Android client SDK for communicating with OAuth 2.0 and OpenID Connect providers.
https://openid.github.io/AppAuth-Android
Apache License 2.0
2.83k stars 884 forks source link

IdToken validation #311

Open Beetin opened 6 years ago

Beetin commented 6 years ago

Is it correct to say that the library currently doesn't do any id_token validation (verifying nonce, verifying signature, verifying issuer).

Isn't this an absolute requirement for oidc before trusting and using the id_token? The id_token could otherwise be MITM changed during the redirect from the browser back to the app (say with a different email/id) and still be accepted.

I saw #163 and #25 , but neither seem to be open anymore.

What is the current state of JWT validation for the library? I didn't see anything in the README or example app.

iainmcgin commented 6 years ago

In most cases, the JWT acquired by the client is sent on to another backend. It would then be that backend's responsibility to validate the JWT. The client cannot simply "assert" that the JWT is valid to a backend, as the client cannot be trusted. For server validation of JWTs, there are a variety of libraries available.

In the rare cases where a client needs to validate the JWT for its own purposes, a separate library such as JOSE4J can be used for this purpose.

brockallen commented 6 years ago

@iainmcgin -- from my reading of the specs, id tokens are for clients, access tokens are for APIs/backends. Where in the specs do they discuss native clients sending id tokens to backends in order for the client to verify the authentication event and establish the user's identity? This seems onerous to me.

iainmcgin commented 6 years ago

It depends on what you're defining as the client. Most applications I've seen that use ID tokens are using them for the purposes of proving the identity of a user in the context of a known identity provider, and this proof is then used to create a new account / sign in to an existing account in the context of the relying party. The backend must not blindly trust the client, and so ID tokens provide a self-contained, tamper proof, verifiable statement of identity for the app to send to the backend. With this kind of authentication flow, I think of the backend as the real client to the identity provider, with the mobile app simply serving as a conduit for the acquisition of tokens that the backend will use.

Mobile apps without some form of backend that still need verified assertions of identity are rare, in my experience. Furthermore, these clients already verify that they are talking to the right identity provider - the app and/or user's browser are checking the certificates associated with the server. The information returned in the JWT is roughly equivalent to what a UserInfo endpoint would return, and the authenticity of the information returned by this endpoint is determined by TLS.

So, I believe it is rare a mobile application to directly consume, and therefore need to validate, ID tokens. This is why I don't believe the additional code and complexity of supporting this needs to be directly added to AppAuth; for those clients which need this functionality, they can simply pass the ID token that AppAuth retrieves to an ID token verification library as previously discussed.

iainmcgin commented 6 years ago

@ve7jtb @WilliamDenniss am I missing anything here?

brockallen commented 6 years ago

Ok, I suppose that's fair. It's just that in all native client app dev work I do, I actually do the work to fully implement the OIDC protocol. I checked the description of this repo, which is:

Android client SDK for communicating with OAuth 2.0 and OpenID Connect providers.

And that's the assumption I incorrectly made. It doesn't state that this repo provides an OIDC implementation (which it doesn't do the the lack of id token validation). This repo only facilitates communication.

But, IMO, if some other library or sample did actually implement OIDC, then it would prove to be more useful than this one. Even in my JS client library that targets browser clients I implemented the full id token validation (https://github.com/IdentityModel/oidc-client-js/). It's OIDC certified even. So it's certainly possible and I'd not say the complexity is too much of a barrier. The benefits would be the consumer of this wouldn't then expect to be a protocol/spec expert, know to provide these missing pieces, and this library would guarantee that it's done right.

It's unfortunate because when customers ask me about libraries written for native mobile platforms (including JS) I point them here.

brockallen commented 6 years ago

And also, when you say backend for the native app is normally your client -- in system design I do, the backend for the native app is normally modeled as the resource server. So the native app is the client using the resource server. So in short, the audience of the id token is the native app, and the audience of the access token is the backend.

Beetin commented 6 years ago

I agree with brockallen.

When I came across this repo, I assumed it was a complete OIDC implementation of a client. In effect, this is still doing all the work of the client EXCEPT token validation.

Android client SDK for communicating with OAuth 2.0 and OpenID Connect providers.

But the security is often part of the communication (or rather a required goal of the communication), rather than separate from it. The library already implements other security features (states, nonces, the extra PKCE protection, etc). To leave out token validation feels like an unexpected omission rather than something done on purpose.

The most clear way to see this, to me at least, is that you have dynamic registration already set up, completely uninvolved from any backend server. So the app is registering ITSELF as a client of the provider, complete with a shared secret. It is storing that secret, creating requests, sending them, receiving the response, maintaining states, using the well known to create URL mappings, validating responses, refreshing the token whenever needed etc etc etc etc. It is a full client, except for token validation.

I'm not even sure the backend could do OIDC compliant token validation. It can check the signature, but how will it get the nonce that the client created for the request (since this is part of the token validation). How will it check the client_id when the client dynamically registered, or are users of the project expected to know that they should also send this information to their backend and store it?

The token validation should be done by the client. The audience of the token is the app. The native app is the client.

iainmcgin commented 6 years ago

If you can submit a compact, clean implementation of ID token validation with no dependencies, we will accept it. The existing JWT validators listed on jwt.io all appear to pull in at least 1-3MB of additional dependencies, such as Jackson and Bouncy Castle. AppAuth is currently 120KB, with a single dependency on the custom tab support library, so adding any of these other libraries would result in a 10x increase in code size for a feature that we believe is rarely used, and can be easily handled directly by the applications that need it.

WilliamDenniss commented 6 years ago

One point of order for this discussion, as we need to be very explicit when referring to validation requirements: validating the ID token is not the same thing as verifying the signature. The latter is a component of the former, and not always required.

The following is what the OpenID Connect Core spec says in section 3.1.3.7 about verifying the signature for code flow clients that are protected via TLS (emphasis added).

If the ID Token is received via direct communication between the Client and the Token Endpoint (which it is in this flow), the TLS server validation MAY be used to validate the issuer in place of checking the token signature. The Client MUST validate the signature of all other ID Tokens according to JWS [JWS] using the algorithm specified in the JWT alg Header Parameter. The Client MUST use the keys provided by the Issuer.

AppAuth only supports the code flow (for this reason), hence we can use TLS server validation and execute that MAY condition to remain compliant.

That said, many of the other steps do apply for native clients. https://github.com/openid/AppAuth-iOS/pull/101 is open for iOS to add this validation, at which time OpenID Connect Core section 3.1.3.7 would be fully implemented. I agree that AppAuth for Android should support validating those claims as well.

Regarding the possibility of including a JWT library, as Iain mentioned, the goal of the AppAuth libraries is not to add dependencies beyond what is absolutely needed. For this reason I don't see us adding JWT validation when the spec itself says that TLS validation should be enough. Of course, the developer is free to do this themselves.

brockallen commented 6 years ago

Good point @WilliamDenniss, and I always forget about that stipulation. I normally do hybrid+PKCE for native apps, which is what drove my comment above.

Beetin commented 6 years ago

I missed that TLS option in the OIDC core, really good to know. That does simply validation considerably.

Without requiring signature checking it seems like just having the token response object call to a validator(String idToken) in setIdToken(String idToken) to parse the token and verify they have valid claims (expiry, nonce, issuer, audiance) would make it OIDC compliant on validating the MUSTS for tokens.

Am I missing anything?

iainmcgin commented 6 years ago

@Beetin yes, this sounds right. I'm willing to put in the code to do the basic claims validation, it's the signature verification that is the especially heavy part as far as dependencies go - though, I believe signature verification also be implemented on top of what Android provides natively without extra dependencies. It's really just not a high priority for us as maintainers to do this work.

liamdawson commented 6 years ago

One edge case for the TLS stipulation is that even if you receive the token over TLS, if you persist it to disk and read it back again later, the token may have been tampered with since, so you'd need to validate the signature upon reload to be confident about the contents if you're offline, as the token didn't come over TLS this time. For this reason, I don't think the TLS validation exception obviates the need for this functionality.

Beetin commented 6 years ago

That feels like an issue of insecure storage/retrieval on phones, not the core OIDC mechanics.

For example, you could take an extra ~10 lines of code to first create a private key in the TEE of your device, sign the token before persisting to disk, and check that signature on retrieval to ensure it hasn't been tampered with. Or send the token to a backend for storage/retrieval there. Or any number of secure solutions that aren't writing it to the devices storage insecurely.

Once the token get to you securely, it is up to you to keep it secure, not the OIDC specs. Just like if your backend server wrote tokens into a public google doc instead of a secure database, you wouldn't complain that the OIDC spec has a security flaw.

iainmcgin commented 6 years ago

What is the use case for storing and using an ID token on the client? Once you have convinced your app once that the user is who they say they are through retrieving the ID token, does it provide any further utility to that app?

liamdawson commented 6 years ago

In the case I'm thinking of, the user IdToken may contain a role that will drive whether some parts of the UI are shown or not. That being said, for my case, I'm happy to just treat it as untrusted in general.

That being said, if someone used it to contain claims like a subscriber level, someone with the Android dev tools might be able to modify said claim without tampering with the app at all. (Hopefully they'd use proper server side validation, but if some of the pay gated features are entirely offline, it wouldn't help)

On Mon, 25 Jun. 2018, 09:42 Iain McGinniss, notifications@github.com wrote:

What is the use case for storing and using an ID token on the client? Once you have convinced your app once that the user is who they say they are through retrieving the ID token, does it provide any further utility to that app?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/openid/AppAuth-Android/issues/311#issuecomment-399798416, or mute the thread https://github.com/notifications/unsubscribe-auth/AAuZTx16uycNDZUSjGI1wDd11efI7cpiks5uACQDgaJpZM4R7n5l .