Sign in with Apple Security for Linked Account - security

Assuming you have an existing user management/database on a web platform. Sign in with Apple should be integrated for a quicker login and registration process – although it will always create a regular account linked to an email address (just without a regular password). Is it safe to use the (validated) JWT provided by Apple for authenticating?
Signing in (existing account) would be the following steps:
User taps on "Sign in with Apple" in an app
the generated JWT from Apple is sent to the authentication server
the server validates the JWT using the public keys provided by Apple's API endpoint
the server extracts the email from the (validated) JWT and if a user with that email exists, this user is signed in (API returns internal access/refresh token for the session)

I try to craft an answer for iOS apps. But first clarify the question:
"Is it safe to use the (validated) JWT provided by Apple for authenticating?"
The only known JWT we receive from an Authorization task is the "id_token". Other parameters may be JWTs as well, but these are opaque for the client.
The question is now, if we send the id_token to the app server, is it sufficient to just validate the id_token to hand out the client an access token for the app server's domain? Answer: NO!
When using Apple's Authentication Framework for iOS for Sign in With Apple, the Authorization task returns an ASAuthorization value in the completion handler. This contains basically the following parameters:
user: an identifier
identityToken: JWT the "id_token" (see OIDC)
authorizationCode: A short-lived, one-time valid token that provides proof of authorization to the server component of the app. The authorization code is bound to the specific transaction using the state attribute passed in the authorization request. The server component of the app can validate the code using Apple’s identity service endpoint provided for this purpose. *)
*) If that value does correspond to the OIDC "code" value which will be obtained by a client via the "front channel" aka user agent aka browser, then we should also ensure that an additional mechanism is in place which actually provides a secure "proof of authorization" (Universal Links, PKCE), see Authorization Code Interception Attack.
If these attacks are technically impossible, because the authentication system provides secure communication channels with the app, we don't need PKCE, though.
The id_token contains information about the user that has been authenticated which is stored on the Provider. It's a signed JWT. Even if the JWT can be successfully validated, with the JWT alone the app server cannot be sure that the sender is the one who it believes it is. We don't want to give anyone an access token who is not authenticated!
The app server needs more prove and this will be accomplished with the authorizationCode parameter. This check has to be done on the Provider though.
So, we have to perform two steps:
Verify the Identity Token (id_token)
This will be performed on the app server.
Validate Authorization Code
The second step will be accomplished by your app server obtaining a refresh token form the Providers special endpoints.
With Step 2 we receive a TokenResponse.
If this was successful, we receive an access token and a refresh token. The access token is of no use, but we need the refresh token:
"You may verify the refresh token up to once a day to confirm that the user’s Apple ID on that device is still in good standing with Apple’s servers."
Store this on your app server.
Once after this is all done on your app server you proceed with:
Manage the User Session
After verifying the identity token, your app is responsible for managing the user session. You may tie the session’s lifetime to successful getCredentialState(forUserID:completion:) calls on Apple devices. This is a local, inexpensive, nonnetwork call and is enabled by the Apple ID system that keeps the Apple ID state on a device in sync with Apple servers.
A "User Session" will likely require a domain specific access token and refresh token. You will likely verify again Apple's refresh token when the client requires a new access token on your token endpoint.
So, the last step is your app sending the domain specific access token and refresh token to your client.

Related

OAuth clarification

I've followed a training in Go as an introduction to microservices architecture a while ago. Getting back to this project I realise that I need more context as we've been quickly digging into the details of the implementations at the time...
I've drawn a simplified sequence diagram of 2 simple use cases:
The user logs in
The user is already logged in and make a purchase
(you can comment / modify the diagram at your convenience)
https://drive.google.com/file/d/1gWgkhJipUvWrVidkl7YFt_xlDmZYn_CX/view?usp=sharing
Here are the questions I have:
Here we're dealing with user authentication but what about client authentication? In the case of a web front end client, can I imagine storing an api_key and an api_secret in the env variables for the server that will be hosting this client? Because there use cases where the user is not logged but some services still needs to be available, but at the same time I only want my known clients (the web front and the mobile app) to be able to access those services (putting aside API Gateway solutions, and maybe other API Shields which would probably add another security layer against DOS etc.)
If the client logs in using Google/Facebook, the front app will receive an id_token that needs to be passed to the backend which would then verify the token ( https://developers.google.com/identity/sign-in/web/backend-auth ). In this particular case my OAuth API would not be used. Could please you confirm that it's the way it should be handled?
Many thanks.
EDIT 02/05/2022
Intro / Context
First thing first, Authorization is not Authentication.
Authentication is the process of verifying who a user is,
while authorization is the process of verifying what they have access to.
And like #Max said, OAuth is designed to manage Authorization and Open ID Connect (OIDC) is an extension of OAuth to manage Authentication on top of it.
The diagram I've exposed in my question is known in the OAuth world as the password grant, and, from the official documentation :
Because the client application has to collect the user's password and
send it to the authorization server, it is not recommended that this
grant be used at all anymore.
Authorization of my App (to get access to my APIs)
From the API perspective, we just want to ensure that the incoming requests are coming from the server that is hosting the App. So, in my case, it's simple machine-2-machine communication from backend server to backend server and there's no action required from the user. So, I must implement the Client Credentials Flow
...which would lead me to this flow:
https://drive.google.com/file/d/1qE9JpWRSRPa8z5iNxm7ocGkeT0E149Sv/view?usp=sharing (Feel free to comment / rectify )
Authentication of a user
Because OAuth knows nothing about authentication, I need an OIDC flow. The easiest one is based on the Authorization Code Flow with PKCE from OAuth below (only about authorization) ...
... but the difference is that we're passing an additional scope named openid in the authentication request (step 3), when the app performs the 2nd request to the token endpoint (step 7), the auth server returns an ID Token (which is a JWT containing user info in the payload -> authentication) in addition to the access_token (which do not contain user info but just "random string"). There's other OIDC flows with their pros & cons depending on the situation but it's another topic on its own (https://openid.net/specs/openid-connect-core-1_0.html)
User already identified by Google/Facebook
In case the client logs in using Google, the front app will receive an id_token. This token could be sent to the app server which would then make a request to the API Gateway, which then call the Auth api which would be in charge of verifying the token by calling the 3rd party auth server ( https://developers.google.com/identity/sign-in/web/backend-auth ).
In case of Facebook, we get back an access token, so I don't know how to deal with it ...
https://developers.facebook.com/docs/facebook-login/web
https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow
Using Firebase, there's an onAuthStateChanged callback, so from the App perspective it will prevent request without the user being logged in, but from the API perspective, it doesn't guaranty that a request is coming from a logged in user...
https://firebase.google.com/docs/auth/web/manage-users#get_the_currently_signed-in_user
Warning: the answer below is not complete, it only serves to give a rough idea
Intro
OAuth2 is a protocol for authorization.
Grant Types
Over the OAuth2 protocol, you can use one of the "grant types" or "flow", one of these flows is illustrated in the picture you post and named password grant.
Each of these flows is realized for different scenarios, you rightly point the attention to how securely store the password on a web app.
For example for a front-end authentication (javascript / wasm) you can use a PKCE Flow where the secret_id is not used.
Endpoints
On OAuth2 there are two primary enpoints
Authorize endpoint - where you obtain the authorization code
Token endpoint - where you exchange the authorization code for the token(s)
Tokens
There are two types of tokens on OAuth2
Access Token
Refresh Token
The definition of token on OAuth2 is "an opaque string", you don't need to read it.
The access token is used against the API, this token has an expiration date, when it is expired the system use the refresh token to get another access_token whitout user interaction, when the refresh_token expire the user must re-authenticate again.
You can read the content of the access_token (which is a JWT token) from the JWT.io
Scopes
The Access token has, on its body, the scopes (i.e. Read email, read name, etc).
Scope is a mechanism in OAuth 2.0 to limit an application's access to a user's account.
Identity
On top of the OAuth2 are build other protocols OIDC aka IdToken aka Open Id Connect is one of them, in other terms OIDC protocol use the OAuth2 for establish an Authentication.
With the OIDC there is another token the id_token, this token came with the user's info and is NOT used has Authorizization in front the API.
There are also OIDC flows you can use to get the id_token and/or the access_token.
Conclusion
I suggest you read about OAuth2 from the references below and try different flows using the playground
References
Which oauth2 flow should I use
OAuth2
PKCE in more depth
My advice is to start with the data, which is the deeper area of OAuth.
USE AN AUTHORIZATION SERVER
This will enable you to keep your code simple. It will also handle Google / Facebook and many other forms of authentication for you, with zero impact on your code. The Curity Community Edition is a free and developer friendly option, though there are others. eg Keycloak, Ory Hydra.
PROTECT DATA
OAuth primarily gives you modern ways to protect data. Use scopes and claims to protect data across multiple microservices in a zero trust manner while securely maintaining user context. You will also need to manage joining identity and business data.
IMPLEMENT UI FLOWS CORRECTLY
Mobile apps use the AppAuth pattern. The current best practice for browser based apps is a Backend for Frontend approach. Both of these are tricky.
KEEP CODE STANDARDS BASED
All of the above Curity resources are based on OAuth related standards. If followed your apps will stay simple, with portable code, that can also work with other providers.
OAuth is very architectural though, and the best designs take time to learn, but you can learn the intricacies gradually. Our IAM Primer is a good starting point.

How does OpenID Connect deal with service chains?

Background
Say I have an application to create shipments. A user sits down in front of that application and loads the page. They are then redirected to the IDP to login. The flow I use is the Authorization Code flow. This involves a Client ID and Client Secret. The IDP can take those values along with the User's Credentials and do the login.
After the login, the application gets an id_token that lets the application know who the user is (authentication).
The application then needs to call a service (we can call it Service 1). The application can pass the id_token to Service 1 as a JWT bearer token.
Service 1 gets the JWT and can use the signature on it (with the IDP's public key) to verify that the JWT in fact came from an IDP that it trusts.
Problem
This is all great and works just fine. But now Service 1 needs to call Service 2 to fulfill the Shipment Application's request.
This is where things get confusing for me. Service 1 has its own Client ID and Client Secret. And it can get a "Client Credentials" token. But Service 2 needs to know the user that is making the request and a Client Credentials token does not have any user information in it.
The Authorization: Bearer header only allows for one token. But I need space for two:
If I only pass the User's JWT, my JWT looks like the call came directly from the Shipment Application to service 2. (It may be that Service 2 should not even be called directly from the Shipment Application.)
But if I pass only the Client Credentials token of Service 1, then Service 2 is not going to get the user's information.
Either way Service 2 is not going to be happy.
Question
Does OpenID Connect have a way to merge two tokens? Or some other way to allow for chains of service calls to work?
NOTE: I currently am passing both. One in the Authorization: Bearer header and one in a custom header. But because this is not part of the OpenID Connect protocol, it is causing issues when working with 3rd party tools (like OpenAPI (aka Swagger)).
You can look at delegated tokens pattern to issue a new token for Service1 to Service communication.
See:
https://auth0.com/docs/tokens/delegation-tokens
https://www.scottbrady91.com/OAuth/Delegation-Patterns-for-OAuth-20

Should my app issue it's own access tokens, when using external oauth2 provider (facebook)?

I would like to give the users a possibility to login with some external oauth2 provider (facebook) in my app. The client's part is running on mobile device in a native app.
I am not sure which of the approaches below should I prefer ?
Should the client send the user's access token by facebook with each request ? At each request backend asks facebook to validate the access token. Based on the validation's result, backend performs authorization and return corresponding result to the client.
Should the backend ask facebook to validate the access token only at user logon, then issue its own access token, return the access token back to the client and client will use this access token at making requests to the server to avoid contacting facebook at each request ?
I have read some questions about how to implement the auth with facebook and most of the devs are using B, but I haven't seen any explanation why is it good/bad to use A ?
What I see as benefits of the solutions:
backend doesn't need to care about issuing, refreshing, validating access tokens since this is done only by facebook's authorization servers.
this solution seems to be more effective, since it does not require to connect to facebook at each request.
Security tokens issued by Facebook are signed with a digital signature. The API server only needs access to the public key to validate the signature. There's no need at all to contact Facebook after the user authenticates.
A reason to issue your own tokens after the user signed in with Facebook could be to add claims to the token. But obviously having your own authorization server comes at a cost. It's up to you to weigh the pros and cons.
If you do decide to have your own authorization server, make sure not to write your own! There are open source options like Thinktecture IdentityServer.
I will vote for option B and here is my explanation,
Your API must authorise the request every time with some auth token , which cannot be external provider token, in such case anyone with an access token (eg: other developers) of other provider can access your api, basically there is no auth here.
When your sever issue access token, it's easy to validate and when needed could be revoked easily (eg: on password reset)
While authenticating , your server has fully control over issuing access token , so the validation is made only once and doesn't have to do every time while calling the API.

Understanding authentication flow with refresh and access tokens on nodejs app

I know there are already many posts about Oauth, Oauth2, JWT, etc.. I have read many and I more confused than ever so I am looking for some clarification. I will propose my view on the subject and I hope somebody can tell me if my implementation is secure enough or what I am doing wrong and how to improve it.
I am building an API Rest server for serving my resources to my users. Let's suppose it is a bank app where users can deposit, withdraw and transfer money.
I am using nodejs, hapijs, jsonwebtokens, and bcrypt for my server. I want to implement two token authentication flow (Oauth2).
This is the way I am doing it:
User logs in to the auth server by giving some credentials (username and password).
The server verifies the user's credentials, if they are valid, it will grant access to the user and return a refresh token and an access token.
These tokens are saved into the local storage of the browser or mobile device.
The access token:
is signed as a jsonwebtoken.
contains issued date, expiration date (5 min), user data (id, username).
The refresh token:
is signed as a jsonwebtoken and encrypted with bcrypt.
contains a unique identifier
may contain an expiration date
is saved in the database.
As long as the access token is valid, that means, it has not expired and contains valid user data, the resource server serves the user the requested resources.
When the access token is no longer valid, the auth server requests the client to provide a refresh token in order to issue a new access token
The server receives the refresh token from the user, decrypts it, compares it to the one in the database, checks if it has been revoked, and checks its unique identifier.
If the refresh token passes all tests, the server issues a new access token to the client.
If the refresh token fails one test, the server requests the user to re-authenticate.
Notes: I am trying to avoid the usage of cookies.
Questions:
If the user is able to steal an access token, I guess it can also steal the refresh token. So, how can I make the refresh token more secure?
Is my perspective of the Oauth2 flow correct?
What can I improve?
Am I missing something?
The reason OAuth2 is so confusion to many people is because it uses different authentication flows depending on what kind of client is used.
OAuth2 distinguishes two client type, confidential or public. Next to that, there are 2 grant flows that are redirection based (auth code and implicit) which are meant to be used with a browser or browser control.
The other two flows (resource owner password and client credentials) are meant to be used from non-browser apps (CLI, background services, trusted mobile clients).
I've described the different flows and when to use them in more detail in this answer here.

What's "Code substitution (OAuth Login)" threat?

From the OAuth 2.0 Threat Model and Security Considerations draft:
4.4.1.13. Threat: Code substitution (OAuth Login)
An attacker could attempt to login to an application or web site
using a victim's identity. Applications relying on identity data
provided by an OAuth protected service API to login users are
vulnerable to this threat. This pattern can be found in so-called
"social login" scenarios.
As a pre-requisite, a resource server offers an API to obtain
personal information about a user which could be interpreted as
having obtained a user identity. In this sense the client is
treating the resource server API as an "identity" API. A client
utilizes OAuth to obtain an access token for the identity API. It
then queries the identity API for an identifier and uses it to look
up its internal user account data (login). The client asssumes that
because it was able to obtain information about the user, that the
user has been authenticated.
If the client uses the grant type "code", the attacker needs to
gather a valid authorization code of the respective victim from the
same identity provider used by the target client application. The
attacker tricks the victim into login into a malicious app (which may
appear to be legitimate to the Identity Provider) using the same
identity provider as the target application. This results in the
Identity Provider's authorization server issuing an authorization
code for the respective identity API. The malicious app then sends
this code to the attacker, which in turn triggers a login process
within the target application. The attacker now manipulates the
authorization response and substitutes their code (bound to their
identity) for the victim's code. This code is then exchanged by the
client for an access token, which in turn is accepted by the identity
API since the audience, with respect to the resource server, is
correct. But since the identifier returned by the identity API is
determined by the identity in the access token (issued based on the
victim's code), the attacker is logged into the target application
under the victim's identity.
Impact: the attacker gains access to an application and user-specific
data within the application.
Countermeasures:
All clients must indicate their client id with every request to
exchange an authorization code for an access token. The
authorization server must validate whether the particular
authorization code has been issued to the particular client. If
possible, the client shall be authenticated beforehand.
Clients should use appropriate protocol, such as OpenID (cf.
[openid]) or SAML (cf. [OASIS.sstc-saml-bindings-1.1]) to
implement user login. Both support audience restrictions on
clients.
This is rather confusing to me: «the attacker needs to gather a valid authorization code of the respective victim from the same identity provider used by the target client application». What's "the respective victim" and what does "identity provider" mean in this and subsequent uses?
The whole attack description is obscure. I came to understand it as "one shouldn't use OAuth 2.0 to implement user login", but wouldn't that mean that major platforms such as Facebook are vulnerable? And vulnerable to what, exactly?
I probably only need a clarification of some of the terms used in this paragraph.
I found the answer by myself. The wording in this section is a bit confusing, but the attack is quite simple. "Identity provider" is the name for the resource server used to verify the identity of an user.
Basically it's a case of using an authentication code issued for a client application to obtain an access token by a different application. I try to outline the steps in a clearer way.
Attacker registers a malicious client (e.g. an app registered to Facebook).
Victim user is tricked into logging into the malicious client using a "Login with third party" button (e.g. "Login into Facebook") triggering OAuth 2.0 authorization_code flow.
The malicious client gets the authorization_code.
The attacker uses the just obtained authorization_code with another app and obtains access to that app as the victim user.
Step 4 is only possible if authorization_codes aren't bound to a specific client. Auth codes issued to a client can only be used by that same client to obtain an access token.
Of course Facebook is not vulnerable, as this requires only a basic check from the authorization server to defeat.

Resources