According to https://dev.twitter.com/docs/api/1/post/oauth/request_token, it is strongly recommended you use HTTPS for all OAuth authorization steps. However, I found out the callback url is not following this suggestion. Is there a difference if a https callback url is given vs a http callback url is given?
For example,
Request URL:
POST https://api.twitter.com/oauth/request_token
Authorization Header:
oauth_callback="http%3A%2F%2Fmyapp.com%3A3005%2Ftwitter%2Fprocess_callback",
The callback URL just needs to in a url format, the Oauth spec does not mandate that it is https.
Certain Oauth providers (like SalesForce) do force the use of https.
Most service providers however just force you to register you callback url so that the service provider can verify that the correct callback url is provided during an oauth dance using your consumer key / secret.
Also, mobile apps in Android can create special protocol handlers, allowing callback handlers like "linkedin://callback" to be defined (to let the Android app handle linkedin oauth callbacks).
In that scenario it wouldn't make sense to force https, as it would force mobile apps to off-load their oauth dance to a remote server, where in some cases its perfectly acceptable to do it in the app itself.
Also keep in mind that whe the access token is compromised, people wishing to make secured api calls also need access to the consumer key / consumer secret.
Related
I try to integrate my Python app(backend client) into the Keycloak system using openid connect, why do I have to fill the Valid Redirect URIs field? From my understanding for OAuth 2.0, the backend channel just need to exchange the code for id and access tokens in the Authorization Code Flow process
Why do we need a redirect URL here, shouldn't it only filled in the front end client?
Any Ideas?
We need it to restrict to what URL we are allowed to redirect to after a successful login. We do this to improve the security for your users, because it would be a big security problem if the provider could redirect to any URL after login.
The client which receives the authorization code and the one which exchanges the code for tokens must be the same client - at least from the Authorization Server's point of view. Theoretically you can have separate applications which handle parts of the OAuth flow, but they must be using the same client ID.
Ideally, the redirect URI points to an endpoint exposed by your Python backend. After Keycloak (or any Authorization Server) finishes authentication and authorization, it will redirect the user to that endpoint together with the authorization code. This way the authorization code will go straight to the backend client, the frontend app will never have to handle it. By registering the URL you want to make sure that no one performs authorization using your client ID, but asks to send the code to another application.
I was reading the site Oauth.com trying to understand how implement security in a Single-Page App when i found this statement:
"The only way the authorization code grant with no client secret can be secure is by using the “state” parameter and restricting the redirect URL to trusted clients. Since the secret is not used, there is no way to verify the identity of the client other than by using a registered redirect URL."
If i undestanf correctly, he says that i can verify the identity of my SPA using a registered redirect URL.
Question 1: If I redirect the authorization code to a url (web server), how can i get it (or the access token, or the protected resources) back in my SPA that is running in a browser ?
Question 2: what kind of check can be done in this registered url to verify my SPA identity?
Q1. You receive an authorization code as the login response in a query parameter. Then swap it for an access token. Then call the API with the access token.
Q2. Use HTTPS to prove that you own the redirect URI. Use PKCE to create a one time use secret at runtime.
RESOURCES OF MINE
Maybe these resources will give you a clearer idea - feel free to post any follow up questions ..
SPA Message Workflow
SPA Tutorial + Code Sample
The authorization code is included in the URL as the code query parameter, so you can access it from the SPA (e.g. with window.location.search). It will depend on the OAuth2 provider whether you will be able to exchange that code for an access token from your SPA.
If you use a web server to do the code->token exchange, you'll need to put the tokens in a database that your SPA has access to.
The quote from OAuth.com mentions two security measures, the state parameter, and registered redirect URLs. These prevent two separate attacks:
The state parameter prevents an attacker from using your URL with a stolen authorization code to retrieve an access token for a victim user and associate it with the attacker's account. By verifying that the state parameter in the URL is the same state that you provided to the authorization server, you can be sure that the redirect was generated by the authorization server, and not an attacker.
The registered redirect URL prevents an attacker from using your client ID, but sending users to an authorization URL that, after authorization, will redirect them to the attacker's web server instead of yours, allowing the attacker to retrieve the user's access token. With a registered redirect URL, the authorization server will only redirect to the pre-registered redirect URLs, which you control, and will not redirect to an attacker's web server.
An alternative to implementing this flow yourself, particularly for a SPA, which has limitations in what OAuth grant types are available to it, is to use a managed OAuth service. Xkit, which I work on, is designed to work with SPAs and moves all of the OAuth flow (including security considerations) out of your code.
All of the React Native Twitter Login Clients that I'm finding seem to be hard-coding the TWITTER_CONSUMER_KEY and TWITTER_CONSUMER_SECRET into the the client code, rather than relying on a server to generate tokens and/or a twitter redirect URL.
Is this safe? (e.g. couldn't a consumer then DOS the API with the TWITTER_CONSUMER_KEY, causing the app to be rate limited?)
Is this the correct way to do it?
Is there a better / more secure way?
According to twitter's documentation, it seems like this is NOT the correct way to do this:
"In the event that you believe that your API keys has been exposed, you should regenerate your API keys by following these steps" - Authentication best practices
Examples which specify that the consumer key/secret should be hardcoded:
https://rnfirebase.io/docs/v5.x.x/auth/social-auth#Twitter
https://github.com/GoldenOwlAsia/react-native-twitter-signin/blob/master/Example/TwitterButton.js#L14
Related questions:
Twitter consumer secret really a secret?
Is it a security vulnerability
Yes.
Your app can be rate limited or flagged as malware/spam etc.
Is there a better / more secure way?
Basically only to have your own site auth (oauth2) done correctly and proxy specific requests from your clients, after validation or a simplified locked down site API that is then translated to the Twitter API.
Why is this, Twitter app-only auth supports OAuth2, allows a secure negotiated handshake and then requests made using a Bearer token. In this mode you can make requests on behalf of your App, but without a logged in user. So can't post tweets or see private accounts or read DMs.
For user-auth, Twitter only support OAuth1 and both the App and User are authenticated, but using a model that assumed plaintext http, so can't share a single token. Every single request needs to be made using consumer key/secret and signing the request. So there isn't a way to do this from a javascript client safely.
Is this safe?
Absolutely not. A bad actor can get users to authenticate via Twitter to receive their token credentials and then use your app's consumer key/secret (which would be available in plain text) to masquerade as your app to do all kinds of nasty stuff.
Is this the correct way to do it?
Given the security vulnerability described above, no.
Is there a better / more secure way?
I'm currently in the process of trying to figure out how to securely achieve authentication with Twitter. This involved a lot of reading, but it appears as though it's simply not possible without your own backend. I'll try and explain why:
Your goal is to receive the user's email/Twitter-ID
To achieve (1), you need to send a request to the GET account/verify_credentials endpoint (https://developer.twitter.com/en/docs/twitter-api/v1/accounts-and-users/manage-account-settings/api-reference/get-account-verify_credentials).
To do (2), you need to provide an authorisation header, which is constructed out of several items, including the user's OAuth tokens as well as your app's consumer key/secret. More info here: https://developer.twitter.com/en/docs/authentication/oauth-1-0a/authorizing-a-request.
You retrieve the user's OAuth tokens using the 3-legged OAuth flow
described here: https://developer.twitter.com/en/docs/authentication/oauth-1-0a/obtaining-user-access-tokens. The first step of this process is to send a POST request to the oauth/request_token endpoint (https://developer.twitter.com/en/docs/authentication/api-reference/request_token).
This endpoint itself requires an authorisation header constructed using
your app's consumer key/secret.
Obviously you can't perform step (4) because that implies you would have your consumer secret available in the client; even if it's not hardcoded, it would have to be in memory at runtime, at some point
Once you have your own backend service, one option would be for your client app to open a browser and direct to an endpoint (let's call it /auth/twitter) on this service which will perform all the steps mentioned above.
This same service could also implement another endpoint (/auth/twitter/token) which handles requests to the callback URL, which you set in your Twitter app settings. This callback URL is used as part of the same 3-legged flow. This endpoint would have all the information needed to then go ahead and retrieve the user's email/Twitter-ID.
Finally, /auth/twitter/token can redirect to a custom URL which your client app would need to handle as part of its URL schemes. It can include enough information by way of parameters for your app to continue as needed post-auth.
My web service expects both browser and non-browser clients. And I use token based authentication to make the service stateless.
I store the token as cookies because I want when some Ajax calls initiated from the web page, the token is sent along automatically by the browser.
But for a non-browser client, how to handle cookie? Do I just manage the cookie as a plain HTTP header?
The ideal way to do this on a non-browser client is to use something like the OAuth2 protocol. This protocol was designed to securely handle stuff like this for both browser AND non-browser clients.
Here's how it works:
Your service exposes an OAuth2 endpoint, typically /oauth/token.
Your client sends a form-encoded POST request to /oauth/token, specifying what 'type' of OAuth2 the client is using. In the case of a non-browser client, you'd most likely supply grant_type=password in the body of your POST request.
You also include your username/password to authenticate yourself.
What your client will then get BACK, is an Access Token / Refresh Token that should be securely stored on your client.
If your client is a mobile device like iOS, you'd store the Access / Refresh Tokens in the iOS keychain -- if you're on Android, you'd store those tokens in Shared Preferences. Either way -- these are 'secure' storage locations meant for holding confidential information like tokens.
Then, when you need to use those tokens to authenticate against your service, you can simply POST to your API with that token, and bam, you will be authenticated.
This is the ideal way to handle token stuff in most situations. I'm the author of an authentication library in Node called express-stormpath, and this is how I do it as well.
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.