Rails API authentication - sanity check and advise - ruby-on-rails

I want to create a Rails application which exposes an API to be consumed by only authorised client applications (will be mobile apps for iOS / android). I've not started working on the app yet, but the primary method of accessing the underlying data will be through the api. I've been looking at using the grape gem, but would need to add an authentication layer to it. I was thinking about using devise and adding another model for storing client details, api key and secret key. Upon sign in through the api, the api key and secret are returned. The API key is transmitted with each request, but the secret key is not. Instead, it is used to sign each request; the request parameters are ordered by name, hashed using the secret key as the hash key. This signature is then added as a parameter to the request.
Does this system of authentication sound logical and secure?
I tried to prototype the system earlier, but ran into difficulty signing up a user using JSON with devise. At first I was getting a CSRF error. I then turned off protect_from_forgery and was getting another error. Is it safe to turn this off if I am authenticating in this way?

Yes you can turn off rails CSRF protection since you are using a different authenticity method as long as a date or timestamp is always inside the parameters that are being signed. You can use this to compare the request time to the server time and make sure you aren't undergoing a replay attack.

protect_from_forgery helps you protect your HTML forms. If you're consuming JSON from mobile clients, you don't need it.
Here's what I would do if I were you:
on user's account page, have a button that says "(re)generate API key"
client then embeds this key into his calling code and passes with each request.
your API server checks whether this API key can be used with this client id.
Very easy to implement and serves well.
Signing parameters also works and I used it in several projects with success. But it increases code complexity without any real gain (secret key is on the client, attacker already knows it).

Related

Twinfield do you need sessions if using oAuth?

I’m updating a third party app that currently integrates with Twinfield using the session’s method with username and password to use the oAuth method.
In confused by the documentation though... do I still need to use the sessions or when using oAuth do I just call the endpoint(s) by passing the access token in the header as normal?
Also their Soap definition has four properties, the usual ClientID and Secret but also accessSecret? What’s that?
TLDR: you no longer need to use the sessions and SelectCompany; when you have the access token you can use that and the company code directly in the header.
You can obtain the access token as described here.
The documentation is a bit unclear on how to use the access token in your calls.
In the old username/password/session flow, you referred to a SessionID in the SOAP Header, and you would do a SelectCompany call to select the relevant target ("administratie").
In the OAuth flow, the SessionID is no longer relevant. Once you obtained a valid access token, you should set that in the header using the AccessToken field.
Instead of the old SelectCompany call, you can set the CompanyCode directly in the header. So if you have obtained an access token eyWhatANiceToken, and want to retrieve data for company "My Company BV [130001]" you have set AccessToken to eyWhatANiceToken and CompanyCode to 130001 in the header.
You can request the available codes using the list offices call

How does keycloak determine which signature algorithm to use?

I'm writing an application that uses keycloak as its user authentication service. I have normal users, who log in to keycloak from the frontend (web browsers), and service users, who log in from the backend (PHP on IIS). However, when I log in from the backend, keycloak uses HS256 as its signature algorithm for the access token, and thus rejects it for further communication because RS256 is set in the realm and client settings. To get around this issue, I would like to "pretend to be the frontend" to get RS256 signed access tokens for my service users.
For security reasons, I cannot give the HS256 key to the application server, as it's symmetrical and too many people can access the server's code.
I am currently debugging the issue using the same user/pw/client id/grant type both on the frontend and the backend, so that cannot be the issue.
So far I have tried these with no luck:
copying the user agent
copying every single HTTP header (Host, Accept, Content-Type, User-Agent, Accept-Encoding, Connection, even Content-Length is the same as the form data is the same)
double checking if the keycloak login is successful or not - it is, it's just that it uses the wrong signature algorithm
So how does keycloak determine which algorithm to sign tokens with? If it's different from version to version, where should I look in keycloak's code for the answer?
EDIT: clarification of the flow of login and reasons why backend handles it.
If a user logs in, this is what happens:
client --[login data]--> keycloak server
keycloak server --[access and refresh token with direct token granting]--> client
client --[access token]--> app server
(app server validates access token)
app server --[data]--> client
But in some occasions the fifth step's data is the list of users that exist in my realm. The problem with this is that keycloak requires one to have the view-users role to list users, which only exists in the master realm, so I cannot use the logged in user's token to retrieve it.
For this case, I created a special service user in the master realm that has the view-users role, and gets the data like this:
client --[asks for list of users]--> app server
app server --[login data of service user]--> keycloak server
keycloak server --[access token with direct granting]-->app server
app server --[access token]--> keycloak server's get user list API endpoint
(app server filters detailed user data to just a list of usernames)
app server --[list of users]--> client
This makes the the list of usernames effectively public, but all other data remains hidden from the clients - and for security/privacy reasons, I want to keep it this way, so I can't just put the service user's login data in a JS variable on the frontend.
In the latter list, step 4 is the one that fails, as step 3 returns a HS256 signed access token. In the former list, step 2 correctly returns an RS256 signed access token.
Thank you for the clarification. If I may, I will answer your question maybe differently than expected. While you focus on the token signature algorithm, I think there are either mistakes within your OAuth2 flows regarding their usage, or you are facing some misunderstanding.
The fact that both the backend and frontend use "Direct Access Granting" which refers to the OAuth2 flow Resources Owner Credentials Grant is either a false claim or is a mistake in your architecture.
As stated by Keycloak's own documentation (but also slightly differently in official OAuth.2 references):
Resource Owner Password Credentials Grant (Direct Access Grants) ... is used by REST clients that want to obtain a token on behalf of a
user. It is one HTTP POST request that contains the credentials of the
user as well as the id of the client and the client’s secret (if it is
a confidential client). The user’s credentials are sent within form
parameters. The HTTP response contains identity, access, and refresh
tokens.
As far as I can see the application(s) and use case(s) you've described do NOT need this flow.
My proposal
Instead what I'd have seen in your case for flow (1) is Authorization Code flow ...
assuming that "Client" refers to normal users in Browser (redirected to Keycloak auth. from your front app)
and assuming you do not actually need the id and access tokens back in your client, unless you have a valid reasonable reason. As the flows allowing that are considered legacy/deprecated and no more recommended. In this case, we would be speaking of Implicit Flow (and Password Grant flow is also discouraged now).
So I think that the presented exchange (first sequence with points 1 to 5 in your post) is invalid at some point.
For the second flow (backend -> list users), I'd propose two modifications:
Allow users to poll the front end application for the list of users and in turn the front-end will ask the backend to return it. The backend having a service account to a client with view-roles will be able to get the required data:
Client (logged) --> Request list.users to FRONTEND app --> Get list.users from BACKEND app
(<--> Keycloak Server)
<----------------------------------------- Return data.
Use Client Credentials Grant (flow) for Backend <> Keycloak exchanges for this use case. The app will have a service account to which you can assign specific scopes+roles. It will not work on-behalf of any user (even though you could retrieve the original requester another way!) but will do its work in a perfectly safe manner and kept simple. You can even define a specific Client for these exchanges that would be bearer-only.
After all if you go that way you don't have to worry about tokens signature or anything like that. This is handled automatically according to the scheme, flow and parties involved. I believe that by incorrectly making use of the flows you end up having to deal with tricky token issues. According to me that is the root cause and it will be more helpful than focusing on the signature problem. What do you think?
Did I miss something or am I completely wrong...?
You tell me.

Validating jwt on native platforms

I'm building an native iOS app, it uses OAuth 2.0/OIDC for authentication and authorisation. The auth server is identity serverver 4.
By going thru documents such as https://www.rfc-editor.org/rfc/rfc8252 I have established that the correct flow to use is "authorisation code" flow even though we own the app, the auth server and the resources.
I also learned that we need to use a secure browser such as SFSafariViewController and that we need to use PKCE and remember to use the "state" key in the request and validate on return.
My problem is validating the jwt on the iOS device. I use https://github.com/kylef/JSONWebToken.swift as suggested on jwt.io
To validate the validity of the jwt we need to check that it was is deed signed by our auth server. The server signs using an async rs256 key and exposes the public key on a endpoint. JSONWebToken.swift does not support rs256 and I have not been able to find any iOS library that does, so how to other people validate jwt on iOS devices? I guess we could swith to HS256 which is supported by JSONWebToken.swift but this is a sync algorithm and would require us to store the key on the device which would not be safe.
How to solve this issue, surely I'm not the only one having it...
You could use the Vapor package at https://github.com/vapor/jwt which does support RS256, but you'll need to fetch the JWK yourself.

django csrf for api that works with ios apps

I am building an ios app that communicates with the server for getting the data.
If its just a normal app, I can send csrf token via forms (since all from same domain). But, for ios apps, I dont think I can set csrf token .
So, when making requests from ios apps, to the server, I am getting error regarding csrf. So, whats the solution for this? Disabling this csrf feature or some other better way ? This is my first ios app, so please tell me a better way so i will follow that.
For those URLs ("API end points") that your iOS app is accessing, you will need to specify #csrf_exempt on the corresponding view functions to disable csrf protection.
More details here - https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#django.views.decorators.csrf.csrf_exempt
And protect those urls via other authentication methods, such as session authentication.
For your authentication purposes, you can easily take reference to what django rest framework and django tastypie has done. Both use SessionAuthentication classes to handle authentication and protect the exposed urls (API endpoints) that your iOS app can connect to.
References:-
http://django-rest-framework.org/api-guide/authentication.html
https://django-tastypie.readthedocs.org/en/latest/authentication_authorization.html
Django tastypie also has an authorization class, which is not to be confused with authentication. It also has an APIKey authorization class which becomes useful when you do want to expose your django URLs to other 3rd party developers who may want to build an app of their own to talk to your django URLs to access data (think "facebook APIs"). Each 3rd party developer can in essence be provided a unique API and because you have the APIKeyAuthorization class and a unique API Key provided to each 3rd party app, you can be sure that only "authorized" apps can consume your django URLs. This is the essence of how various big platforms like "Google+" or "Facebook" etc work.
Details of how django's csrf works
https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#how-it-works
The CSRF protection is based on the following things:
A CSRF cookie that is set to a random value (a session independent
nonce, as it is called), which other sites will not have access to.
This cookie is set by CsrfViewMiddleware. It is meant to be permanent,
but since there is no way to set a cookie that never expires, it is
sent with every response that has called
django.middleware.csrf.get_token() (the function used internally to
retrieve the CSRF token).
A hidden form field with the name ‘csrfmiddlewaretoken’ present in all
outgoing POST forms. The value of this field is the value of the CSRF
cookie.
This part is done by the template tag.
For all incoming requests that are not using HTTP GET, HEAD, OPTIONS
or TRACE, a CSRF cookie must be present, and the ‘csrfmiddlewaretoken’
field must be present and correct. If it isn’t, the user will get a
403 error.
This check is done by CsrfViewMiddleware.
In addition, for HTTPS requests, strict referer checking is done by
CsrfViewMiddleware. This is necessary to address a Man-In-The-Middle
attack that is possible under HTTPS when using a session independent
nonce, due to the fact that HTTP ‘Set-Cookie’ headers are
(unfortunately) accepted by clients that are talking to a site under
HTTPS. (Referer checking is not done for HTTP requests because the
presence of the Referer header is not reliable enough under HTTP.)
This ensures that only forms that have originated from your Web site
can be used to POST data back.

Authenticating against a REST API with iOS client using Facebook SSO as the only login mechanism

I'm planning to use Facebook as the only sign-on mechanism for an application that I'm building and need some feedback on the design. Here it goes -
User opens the app and is presented with a register screen. The facebook authorization flow starts and let's assume it succeeded and the user has successfully registered himself. Upon success, the app calls the Facebook graph API and gets the user's firstname, lastname, email, date of birth etc. With this data, the app then calls a web service method called RegisterUser(string Fullname, string FirstName, string LastName ...) which creates the user record in the database.
Now for subsequent calls to the API, I need to authenticate that the request is really coming in from my application (not necessarily a particular user). I've looked up the S3 REST API and it seems that with every request there's a HTTP header called Authorization that the client creates by appending a bunch of other HTTP Headers like Date, Method, Request data, signing it with the client's private key and computing its base64 encoded value. This is verified on the server side to authenticate the client.
Now, I'm comfortable implementing all this, but a few questions:
If I have a private key, is it safe to include it as a part of the iOS application itself? Can someone extract the key from the iOS application binary? If so, how do I deal with this?
Are there any other changes you'd make to this design ?
Thanks,
Teja.
Make sure you apply a one-way hashing algorithm to the value to base64 encode - base64 is a two-way encoding, so you don't want eavesdroppers reverse engineering your private key from that. Amazon S3 does this with performing a SHA-1 before doing the base64.
As with all (AFAIK?) compiled binaries, your app shouldn't be able to be decompiled.

Resources