What is purpose of Client secret if it is not used for Google SignIn in React Natvie (ios/android) app having Backend api? How is this secure? - react-native

I have react native app(lets say ABC) from where I am going Google sign in, I get the idToken and send it securely using Https to backend server, it then validates idToken and gets accessToken. But in this entire process clientSecret is not used anywhere. My Client Ids can be used by anyone, let's say app XYZ to access user data. How is this secure?
On the other hand in my react website and backend server where I was using passport, client secret was needed. Am I missing something here?
And if the process used in native app is secure, I can use same process in react website, i.e. get rid of passport strategy.

Client ID is included in HTML served to browsers, as you point out it's not secure--nor is it intended to be. The Client ID simply identifies the app.
For Web apps Authorized JavaScript origins are required, and for back end services Authorized redirect URIs are also necessary. Requests/responses are verified by Google's OAuth service using these settings.
HTTPS, which includes a signed certificate is required and helps ensure traffic originates from source hosted and served from your verified domain.
For sign-in responses typically use a signed JWT that can be securely verified on the back end (unless OAuth 2.0 or gapi is used).
Client Secret, should of course never be included in HTML served to a browser and should be kept only on a secure location, out of source control, and on the back end server.
Use of a nonce would also help you avoid replay attacks. See the
state variable for pure server side apps, or Sign In With Google nonce attribute.

Related

How do I allow users to link with Spotify without exposing my Spotify client id & secret on the frontend?

Here's my setup. I have a NodeJS express server providing endpoints and hosting my frontend which I've built using React. I have the Spotify client id & secret stored in a .env file which the frontend references, and therefore publicly exposes.
Currently, for users to link with Spotify, the frontend can redirect to Spotify's authorize page and pass the client id and secret (and a redirect uri) in the url params. They then log in with their Spotify credentials and accept my app's terms. Then Spotify then redirects them to the provided redirect uri, which is just another page of my React app. The user's refresh token is passed as a url param for my frontend to receive. It then sends refresh token to my server using one of my endpoints and I store it in my database under their account.
This works fine, except for the fact that my app's client id and secret are publicly exposed through my frontend. I'm trying to work out a way to allow users to link with Spotify without having the frontend know this information, because if it leaks then people can make calls to Spotify's API on my behalf. But I can't seem to get around the fact that the client's browser needs to at some point have access to something like this.
const url =
'https://accounts.spotify.com/authorize?' +
querystring.stringify({
response_type: 'code',
client_id: spotify_client_id,
scope: spotify_scope,
redirect_uri: spotify_redirect_uri
})
window.location.href = url
I'm new to web development so there may be something obvious I'm neglecting. If anyone has any ideas, I'm all ears. Thanks in advance!
In this particular scenario, you’ve designed around the entirely wrong OAuth flow for the job. Client credentials-style authentication/authorization is not intended to be used in the manner you describe, for the reasons you describe. Instead, you should be using the offered authorization code with PKCE flow, which provides similar functionality for web apps, etc. without necessitating the exposure of your sensitive authentication secrets.
Spotify is pretty explicit about this in their documentation (emphasis mine):
Which OAuth flow should I use?
Choosing one flow over the rest depends on the application you are
building:
If you are developing a long-running application (e.g. web app running on the server) in which the user grants permission only once,
and the client secret can be safely stored, then the authorization
code flow is the recommended choice.
In scenarios where storing the client secret is not safe (e.g. desktop, mobile apps or JavaScript web apps running in the browser),
you can use the authorization code with PKCE, as it provides
protection against attacks where the authorization code may be
intercepted.
For some applications running on the backend, such as CLIs or daemons, the system authenticates and authorizes the app rather than a
user. For these scenarios, Client credentials is the typical choice.
This flow does not include user authorization, so only endpoints that
do not request user information (e.g. user profile data) can be
accessed.
The implicit grant has some important downsides: it returns the token
in the URL instead of a trusted channel, and does not support refresh
token. Thus, we don’t recommend using this flow.
It may go without saying, but since you’ve already elected to publicly publish your app secret, you should consider it compromised and invalidate it immediately before malicious actors are able to indeed use it to craft abusive API requests.

Storing client secret on client side

I'm using an external service called auth0 in order to get an access token and let my users use my api. Auth0 is using Oauth2 protocol.
In short The user adds a username and a password, I'm doing a call to auth0 by using a client_id (apps have an id) and client_secret and I get an jwt access token in return. Then from there I carry this access token to have access to my own api since I can check its validity.
I have been looking around about how secure it is to store client_id and client_secret on the client side (e.g. web (javascript)/mobile (native or hybrid with ionic)) and everybody was saying that it's not secure since everybody can reverse engineer the code and get the client_id and client_secret. Ok...I can take it...what Can I do with them if I don't have credentials in order to get the access token?
Given that I don't want to store the client_id and the client_secret, one solutions I have thought is to make a direct call to my api (Java) with the credentials and then my api make a call to auth0 and return the corresponding access token. In this way the client_id and client_secret is stored in the backend and somebody cannot get them easily. Is that safe?
However I have some endpoints, e.g. creating use account, sending sms for phone validation etc, that cannot have credentials. How do I protect the api in such case? If I can't store my own access token on the client side how could I get an access token and access my own api without credentials?
Thanks
One possible solution that OAuth spec suggests is that you could have three different servers for your application.
client-side
backend server and an additional authentication server.
The preferred way of doing this would be that the client would send the user credentials to the authentication server. The authentication server would be a back-end server which contains the client secret
The authentication server will authenticate the credentials and return back the token.
The client will then use the token obtained from the authentication server to access the resource API server.
If you wanna know more check out this video
https://www.youtube.com/watch?v=rCkDE2me_qk
In my opinion you are almost certainly using the wrong OAuth flow. I use Auth0 with Ionic as both a web app and a native Cordova app. I don't have the client secret in my client code at all.
If you follow the Auth0 quickstarts (https://auth0.com/docs/quickstarts), you should be choosing (Native/Mobile App) if you are deploying to app stores, and (Single-Page App) if you are deploying the web version of Ionic. From there you can pick Cordova (for native) or Angular (for SPA). These should give you instructions that implement OAuth flows which DO NOT require your client secret. My guess would be you are referencing a "Regular Web App" quickstart, which runs server-side and CAN safely hold the client secret. That's not the world you're coding in if you are using Ionic Hybrid/Native.
I would consider wrapping the call to Auth0 into your own server side implementation as safe. Your API takes user credentials and then calls Auth0 and this way your client_id/secret are secure on your server and the client can be reverse-engineered all the way without compromising your security.
Regarding the other APIs which cannot have credentials you are pretty much out of luck. Their very use case is to be used by an unauthenticated third party, so at least the account creation API cannot really be protected. However you can still use some nicely designed constraints to limit the attack surface. E.g. you can require an email address/phone number to register and you will not allow the same address/phone number twice. If you set up your process that you first need to confirm your email address before you can validate your phone number this will make the life of an attacker a lot harder. He would need a real working email address, and some automation to receive your confirmation mails before he could get to call your SMS service. You could also rate-limit the service per IP-address so an attacker cannot cause your SMS cost to skyrocket by issuing a lot of calls for SMS validation in a short period of time.

Secure API for both mobile and web

I have three applications:
REST API
Single Page Web App
Native Mobile App.
Both the web app and the mobile app uses the API for user authentication and fetching user specific data.
My problem is then how to secure this API against CSRF, XSS and other types of attacks.
As I understand, there are two common authentication strategies that each has pros and cons:
Token based authentication
Session/cookie based authentication (secured and httponly)
Session/cookie based authentication
Cookies are vulnerable to CSRF attacks, so to protect against that i would need to implement CSRF tokens, or a similar protection strategy. For a secure CSRF token implementation I should also setup proper CORS policy, so foreign sites can't get a CSRF token from the API. This is also good and well for the web app, however not possible for the mobile app, as mobile apps doesn't support CORS (it has now domain or origin header).
Token based authentication
The alternative is token based authentication, which works well for mobile apps, and removes the need for CSRF tokens as they themselves validates the
authenticity of the request. However there are no secure way to store tokens in a Single Page Web App (locale/web storage is not secure, and if I use cookies, we are basically back to our first problems).
Problem
So my problem is, how should i implement secure authentication for both apps?
My current idea would be to implement both strategies but only allow token auth when origin is not present, and only allow session/cookie auth when origin header is present and allowed by the CORS policy.
But I'm in no way an expert on the subject, and might easily have misunderstood something. Any suggestions or further explanations would be very welcome :)!
Authenticity of the request
The alternative is token based authentication, which works well for mobile apps, and removes the need for CSRF tokens as they themselves validates the authenticity of the request.
Auth tokens cannot be trusted to validate the authenticity of a request, because they only identify the user, not that is the genuine mobile app doing the request.
An attacker controlling the device where the mobile app is running can extract the auth token to automate the requests to the API server. Another technique used by attackers is the creation of a fake captive portal for free wifi(airports, train stations and other public spaces), where they trick who signs in to install a custom ssl certificate in their mobile device, so that the attacker can decrypt the https traffic, thus stealing the auth tokens and perform automated requests to the API server in behalf of the user.
Implementing both strategies
My current idea would be to implement both strategies but only allow token auth when origin is not present, and only allow session/cookie auth when origin header is present and allowed by the CORS policy.
This can be easily bypassed and automated by an attacker. So the attacker would
only make requests with the stolen token and without using the origin in the
headers of the request, thus avoiding the security for web and bypassing the ones
for a mobile app.
Suggestions
But I'm in now way an expert on the subject, and might easily have misunderstood something. Any suggestions or further explanations would be very welcome :)!
I will recomend you to read this series of articles about Mobile API Security techniques, that will give you good insights in how to protect an API. In the article we can see how api-keys, HMAC, certificate pinning, OAUTH can be used to secure the API and also how they can be bypassed. While in the scope of a mobile API some of the techniques are valid in the context of a web app.
For the the web:
Use the Strict Transport Policy header to guarantee that your web app is always loaded over https.
Your web app should use CSP(Content Security Policy) with a report service that will let you know in real-time when any of the policies is violated.
If using cookies you should enable httpOnly flag to protect them from being
accessed via javascript. Further more you want to enable the secure flag for cookies to be sent only hover a https connection. Also try to scope cookies by the path they
belong to, i.e. cookies for a login page should be scoped to the /login path,
thus they will not be sent for other pages or assets(images, css, etc.) of the the web app.
Add recAptcha V3 to all pages of your web app. It runs on the background without any user interaction required and provides a score from 0 to 1, where towards 1 we are more confident that is a human making the request.
Quoting Google:
We are excited to introduce reCAPTCHA v3, which helps you detect abusive traffic on your website without any user friction. It returns a score based on the interactions with your website and provides you more flexibility to take appropriate actions.
That score will allow to have some degree of confidence in blocking non human traffic. If you need more confidence then you may want to use also User Behaviour Analytics(UBA) solutions, that may use machine learning and artificial intelligence for further analysis of the incoming traffic and detection of abusive traffic. Due to the way the web works, both reCaptcha V3 and UBA are not able to provide bullet proof solutions to authenticate requests as legit ones.
For Mobile Apps:
Use a Mobile App Attestation solution to enable the API server to know is receiving only requests from a genuine mobile app.
The role of a Mobile App Attestation service is to guarantee at run-time that your mobile app was not tampered or is not running in a rooted device by running a SDK in the background that will communicate with a service running in the cloud to attest the integrity of the mobile app and device is running on.
On successful attestation of the mobile app integrity a short time lived JWT token is issued and signed with a secret that only the API server and the Mobile App Attestation service in the cloud are aware. In the case of failure on the mobile app attestation the JWT token is signed with a secret that the API server does not know.
Now the App must sent with every API call the JWT token in the headers of the request. This will allow the API server to only serve requests when it can verify the signature and expiration time in the JWT token and refuse them when it fails the verification.
Once the secret used by the Mobile App Attestation service is not known by the mobile app, is not possible to reverse engineer it at run-time even when the App is tampered, running in a rooted device or communicating over a connection that is being the target of a Man in the Middle Attack.
The Mobile App Attestation service already exists as a SAAS solution at Approov(I work here) that provides SDKs for several platforms, including iOS, Android, React Native and others. The integration will also need a small check in the API server code to verify the JWT token issued by the cloud service. This check is necessary for the API server to be able to decide what requests to serve and what ones to deny.
Possible Solution
So my problem is, how should I implement secure authentication for both apps?
For the web app:
OpenID or OAUTH2 for both mobile and web app and maybe using a cookie to store the auth token, scoped by url path, with httpOnly and the secure flag enabled.
Further use strict CSP policies along side with CORS, CSFR, Strict Transport Policy and any other I may be missing now.
Google reCaptcha V3.
For the mobile app:
OpenID or OAUTH2.
Mobile App Attestation solution.
Certificate Pinning.
The API will only accept a request that contains a header with the reCaptcha V3 token or with a mobile app attestation JWT token. Any other requests should be denied.
In the case of a web app to proceed further with the request the recCaptcha V3 score(0 to 1.0) should give the confidence that the request comes from an human, maybe greater than 0.9? You will need to play with the value and monitor it in order to find the correct balance.
For a request from a mobile app to be able to continue being processed, the API server must verify that the JWT token have a valid signature and is not expired.
While the web requests are verified in a best effort basis the requests coming from a mobile app protected with the Mobile Attestation service have only 2 possible outcomes, valid or invalid.

Secure App with Api server with static jwt token

This may have been asked but maybe 2018 there is a better way. I"m trying to secure my api server and only grant access to my client apps I have created. Mobile / Web client apps that is using vue.js for client app and express.js on node for api server.
Would is be wise to create a static token code it in on the client app. It would be sent with every api call. Then on the api server have my secret to the token. Verify the token before api call can be sent back.
Is there a better way? It seems to work fine but maybe there is something I don't know. Maybe everyonce and a while replace the static token and secret? The network would be https for sure. I am sending the token with every api call req.body.clientAppToken
What do you think? Thanks everybody!
Update this is express api middleware below.
App sends a manually created token to the api server on every request. Token is the same for all apps. I verify the token with the secret stored on the server. If it passes they get the data if it does not they do not.
const jwt = require('jsonwebtoken')
const config = require('../config/config')
module.exports = {
authClientAppToken (req, res, next) {
// start see if client app jwt is correct
/* eslint-disable */
console.log('req.body.clientAppToken', req.body.clientAppToken)
try {
let decoded = jwt.verify(req.body.clientAppToken, config.jwtAppSecret)
next()
} catch (err) {
res.status(400).send({
error: 'We got problems'
})
}
/* eslint-enable */
// end see if client app jwt is correct
}
}
I"m trying to secure my api server and only grant access to my client apps I have created. Mobile / Web client apps that is using vue.js for client app and express.js on node for api server.
The cruel truth here is that you cannot truly secure a web app because any kind of secret, be it a JWT token, a signed JWT token, an API Key or anything else you may want to use is very easily retrievable on the client side just by using the browser developer tools, aka F12.
On a mobile app developers may think that once they ship a binary that they can hide a secret there and keep it safe, well mobile apps are not as trivial to look into as using F12 in the browser, but they are easy enough to reverse engineer with tools like the Mobile Security Framework, that will extract all the code in the binary, thus exposing all the secrets stored on it. But even if the secrets are computed at runtime this tool is capable of doing runtime introspection on what your mobile app is doing, thus able to extract also dynamic computed secrets.
Would is be wise to create a static token code it in on the client app. It would be sent with every api call. Then on the api server have my secret to the token. Verify the token before api call can be sent back.
So as said previously any secret generated or stored in the client side can be extracted and reused outside of the scope it intended to. Despite this I still encourage the use of them as 1 more layer of defence.
It seems to work fine but maybe there is something I don't know.
I would recommend you to go through this series of articles on Mobile Api Security techniques to understand how https, certificate pinning, api keys, tokens, HMAC can be used to secure the API server and how they can be bypassed. While the article is in the context of a mobile api, almost everything applies in the context of an API serving a web app.
Maybe everyonce and a while replace the static token and secret?
Using short lived expiration times on tokens is wise and recommend, but they just make the window where they can be malicious reused shorter but they will not prevent API abuse.
The network would be https for sure.
While https should be always used by itself only guarantees that the data in transit is encrypted and cannot be readded by third parties, thus protecting the secrets in transit but will not prevent them to be reverse engineered from the app itself.
Is there a better way?
For web apps you can make it hard by using a javascript obfuscation tool, implement reCaptcha V3 and if you want to go more advanced a User Behaviour Analytics Software solution on your API server.
Regarding mobile apps is possible to build them in a way that is not needed to store secrets in them in order to pass down in each call to the API server. The technique is called Mobile App Attestation and consists of a mobile sdk and a cloud service that work together to ensure that the mobile app is the same one uploaded to Google Play or to the App Store. The mobile sdk runs in the background and communicates with the cloud service that will issue a valid JWT token when the app is the original one and is not running in a jailbroken or rooted device or being object of a man in the middle attack. A valid JWT token is one that is signed by a secret only known by the cloud service and the API server, the mobile app does not know it at any moment, thus is not possible to reverse engineer or fake it. An invalid JWT token only differs from a valid one by being signed with a secret not known by the API server, thus making it indistinguishable from a valid one. This type of security solution can be found at Approov and this is where I currently work.

REST API authentication for web app and mobile app

I'm having some trouble deciding how to implement authentication for a RESTful API that will be secure for consumption by both a web app and a mobile app.
Firstly, I thought to investigate HTTP Basic Authentication over HTTPS as an option. It would work well for a mobile app, where the username and password could be stored in the OS keychain securely and couldn't be intercepted in transit since the request would be over HTTPS. It's also elegant for the API since it'll be completely stateless. The problem with this is for the web app. There won't be access to such a keychain for storing the username and password, so I would need to use a cookie or localStorage, but then I'm storing the user's private details in a readily accessible place.
After more research, I found a lot of talk about HMAC authentication. The problem I see with this approach is there needs to be a shared secret that only the client and server knows. How can I get this per-user secret to a particular user in the web app, unless I have an api/login endpoint which takes username/password and gives the secret back to store in a cookie? to use in future requests. This is introducing state to the API however.
To throw another spanner into the works, I'd like to be able to restrict the API to certain applications (or, to be able to block certain apps from using the API). I can't see how this would be possible with the web app being completely public.
I don't really want to implement OAuth. It's probably overkill for my needs.
I feel as though I might not be understanding HMAC fully, so I'd welcome an explanation and how I could implement it securely with a web app and a mobile app.
Update
I ended up using HTTP Basic Auth, however instead of providing the actual username and password every request, an endpoint was implemented to exchange the username and password for an access key which is then provided for every authenticated request. Eliminates the problem of storing the username and password in the browser, but of course you could still fish out the token if you had access to the machine and use it. In hindsight, I would probably have looked at OAuth further, but it's pretty complicated for beginners.
You should use OAuth2. Here is how:
1) Mobile App
The mobile app store client credentials as you state yourself. It then uses "Resource Owner Password Credentials Grant" (see https://www.rfc-editor.org/rfc/rfc6749#section-4.3) to send those credentials. In turn it gets a (bearer) token it can use in the following requests.
2) Web site
The website uses "Authorization Code Grant" (see https://www.rfc-editor.org/rfc/rfc6749#section-4.1):
Website sees unauthorized request and redirects browser to HTML-enabled autorization endpoint in the REST api.
User authenticates with REST service
REST site redirects user back to website with access token in URL.
Website calls REST site and swaps access token to authorization token.
Here after the website uses the authorization token for accessing the REST service (on behalf of the end-user) - usually by including the token as a "bearer" token in the HTTP Authorization header.
It is not rocket science but it does take some time to understand completely.
3) Restricting API access for certain applications
In OAuth2 each client is issued a client ID and client secret (here "client" is your mobile app or website). The client must send these credentials when authorizing. Your REST service can use this to validate the calling client
I resolved this for my own API quite easily and securely without the need to expose any client credentials.
I also split the problem into 2 parts. API authentication - is this a valid request from a recognised entity (website or native app). API authorisation, is that entity allowed to use this particular endpoint and HTTP verb.
Authorisation is coded into the API using an access control list and user permissions and settings that are set up within the API code, configuration and database as required. A simple if statement in the API can test for authorisation and return the appropriate response (not authorised or the results of processing the API call).
Authentication is now just about checking to see if the call is genuine. To do this I issue self signed certificates to clients. A call to the API is made from their server whenever they want - typically when they generate their first page (or when they are performing their own app login checks). This call uses the certificates I have previously provided. If on my side I am happy the certificate is valid I can return a nonce and a time limited generated API key. This key is used in all subsequent calls to other API endpoints, in the bearer header for example, and it can be stored quite openly in an HTML form field or javascript variable or a variable within an app.
The nonce will prevent replay attacks and the API key can be stolen if someone wants - they will not be able to continue using after it expires or if the nonce changes before they make the next call.
Each API response will contain the next nonce of if the nonce doesn't match it will return an authentication error. In fact of the nonce doesn't match I kill the API key too. This will then force a genuine API user to reauthenticate using the certificates.
As long as the end user keeps those certificates safe and doesn't expose the method they use to make the initial authentication call (like making it an ajax request that can be replayed) then the API's are nice and secure.
One way of addressing the issue of user authentication to the API is by requesting an authentication token from the API when the user logs in. This token can then be used for subsequent requests. You've already touched on this approach - it's pretty sound.
With respect to restricting certain web apps. You'll want to have each web app identify itself with each request and have this authentication carried out inside your API implementation. Pretty straight forward.