Rails public/private key authentication, but user has no server - ruby-on-rails

I've built a JavaScript app that's powered by a Rails back-end. The JavaScript app (the built/minified version) is going to live on an S3 bucket.
The app Iv'e built is going to run on other people's websites, similar to Stripe (Stripe has you include this code in your HTML):
<form action="" method="POST">
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="api_key"
data-amount="999"
data-name="Stripe.com"
data-description="Widget"
data-image="/img/documentation/checkout/marketplace.png"
data-locale="auto">
</script>
</form>
The difference is that my user will have no back-end, it's all handled by my Rails API. The end-user fills out the form on my user's website, and that data is POSTed to my API, not my user's API. My user signs up, posts a snippet with a script to my JavaScript app on their website, and then they have users fill out a form (again, like Stripe) on their website which sends a request to my API.
How can I manage authentication in this circumstance? The key that will be provided in the code snippet is obviously public, given that it's in the HTML on my user's website. How can I verify the request is coming from the correct person given the key? Should I look at IP Address and/or domain name that the request comes from?

Let's pretend your user's website is www.example.com.
You want to know did this request come from the real www.example.com ?
There are two possible solutions :
1- Certificates
If your user's site uses https(it has a certificate) the certificate identifies a unique site(that what it's for), on signup you can request that certificate and add it to your rails trust store, then in your javascript app you send the certificate on every request and your rails verifies it.
advantages
1- no backend needed.
2- more secure than a simple api-key, that's because an api-key could provide authentication but not non-repudiation which the certificate provide.
3-easy for your user(provided they already have a certificate).
disadvantages
1- too secure : you didn't mention that non-repudiation is a goal, if it is this disadvantage doesn't count.
2- might hurt performance : on every request a certificate is posted and verified, not only verifying a certificate is slower than verifying an api-key but sending the certificate on every request may heart bandwidth.
3- requires your user is using https, what if he/she doesn't want to use https for some reason ???
2- Setting a proxy
You store the api-key on the server and don't post it in the snippet and you assemble a simple backend that its only job is to send the api-key (some sort of a proxy).
advantages
1-not too secure : it provides the authentication you want without providing more needless goals.
2- good on performance : yes there is a proxy which may be slower, may be faster but that's faster than verifying a certificate and sending it on every request in my opinion.
3-simpler to program : I think assembling this backend is easier than writing certificate verification code in a Rails app.
4- doesn't require https.
disadvantages
1-more installation for the user : that's because they have to install a backend besides the include snippet.
2-the form data should be sent to this backend and then to your rails backend, so we have two round trips.
Now web servers (Apache,Nginx) can be configured to work as proxies but I don't know if they can be configured to send the api-key too.

Related

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.

Apple Sign-In: How to use it for custom server endpoint authentication?

My use case is that once I have a user signed into my app, I use the Oauth token, resulting from the sign-in, when I make endpoint calls from my app to my custom server-- to authenticate the caller. E.g., I use Google Sign In in this way.
This method (e.g., with Google Sign In) has several useful properties:
Updated tokens are created automatically on the client app.
My custom server can easily verify the validity of the token, using Google's endpoints.
Initial token verification can take place early in the endpoint request processing-- without access to the custom servers database (as in the style in https://github.com/IBM-Swift/Kitura-Credentials).
My question is: Given that we're being told we have to incorporate Apple Sign-In into our iOS apps (if we offer general purpose sign-in facilities), how can I do endpoint authentication with my custom server?
I see two alternatives, neither of which I like very much.
First, I can have my client app send an Apple Sign In id_token to my server and ignore the exp (expiry) field. I can regenerate the id_token periodically (apparently, no more than once a day) and send it back to my client. I don't like this idea both because of ignoring the expiry of the token, and because of the need to periodically send the token from server to client. (My app uses multiple sign in systems and this just creates extra difficulty).
Second, I could have my client send an Apple Sign In refresh token to my server. My server would need, of course, to initially generate that refresh token and send it back to the client. I like this idea even less than the first idea. My initial token verification in my custom server would need to access its database to look for a match this token. I can't generally use an Apple endpoint -- because, again, Apple is apparently going to throttle this verification.
Additionally, I don't really like the idea that my custom server can, at best, check on token validity once a day. If the user revokes the app's credentials, I would hope my custom sever would stop being able to operate on behalf of the user relatively quickly.
Thoughts?
10/5/19-- update to the first alternative above. Upon actual use of https://developer.apple.com/documentation/signinwithapplerestapi/generate_and_validate_tokens for refresh token validation, I find that it is not actually generating an updated id token. It is generating an access token (but Apple doesn't define a use for that), and is validating the refresh token. And so, there is no way to send an updated id token to the client iOS app. Thus, using the first alternative, the expiry date of the id token cannot be used.
10/10/19-- update: I've written a blog article on this subject-- https://medium.com/#crspybits/apple-sign-in-custom-servers-and-an-expiry-conundrum-d1ad63223870
8/6/20-- update: Follow on blog article with possible path forward, pending details from Apple: https://medium.com/#crspybits/part-ii-apple-sign-in-custom-servers-and-an-expiry-conundrum-b3e9735dc079
In Get the most out of Sign in with Apple in WWDC 2020, at 11:30 in their presentation, they introduce server-to-server notifications to enable your server to monitor user account state changes on a real-time basis.
So far, few details on this though.
----------------- UPDATE (12/23/20) -----------------
I now have these server-to-server notifications working in a testing environment with my server. Some notes:
I decided on the endpoint to use, on my server, to allow Apple to send my server these REST endpoint requests.
I pasted that into developer.apple.com > Account > Certificates, Identifiers & Profiles > Identifiers > Select your app identifier > Click 'Edit' next to 'Sign In with Apple' > Server to Server Notification Endpoint
This endpoint is effectively unauthorized. E.g., it is made by Apple with no OAuth credential access to your server. How this is setup will depend on your server. I had a means to set up a new endpoint/route for my server that was unauthorized.
I have the client side and other parts of my server set up to allow creation of accounts using Apple Sign In. So, using one of those accounts, I now started taking actions that would cause Apple to invoke their server-to-server notification endpoint on my server. I wanted to reverse engineer the details of the endpoint request Apple is making, since details are scarce.
This provides some ideas on how to cause the notification events to occur:
How to revoke Sign in with Apple credentials for a specific app?
You can revoke credentials, but it's easier (because you can do it repeatedly) to enable and disable the email relay. Of course, to do this, you have to initially sign-in with Apple using the private/email relay.
I next learned two things:
a) After you take the action (e.g., revoke the email relay), the server-to-server notification endpoint is accessed on your server within about 30 seconds. I had added various log output into my server, so could watch my server log and see this happening.
b) The endpoint request Apple makes to your server has body data containing JSON in the following format:
{"payload" : "-- SNIP -- JWT"}
I'm using the following Swift structure to decode this.
struct ApplePayload: Decodable {
let payload: String // JWT
}
As Apple has indicated in the WWDC 2020 video (https://developer.apple.com/videos/play/wwdc2020/10173/), the main content of the body data is a JWT. Above, this is the value of the key "payload" in the JSON.
The next step is decoding this JWT. I just guessed that it would use the same mechanism for decoding as with the JWT in other parts of the Apple Sign In server-side process. And specifically, in decoding the identity token (a JWT) passed up to your server by a client using Apple sign in. See https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple
I had some code that did this JWT decoding, so I factored that out and put it in a common place:
https://github.com/SyncServerII/AppleJWTDecoder.git
Integrating that into my server-side processing of Apple's server-to-server notification requests, I found that indeed this JWT can be decoded in this manner.
Another aspect that became evident is that the structure indicated by Apple in the WWDC 2020 video isn't 100% what is present in the JWT, after decoding. Specifically, in my tests so far at least the events field is not an array, rather it has a single value. See https://github.com/SyncServerII/AppleJWTDecoder/blob/main/Sources/AppleJWTDecoder/AppleSignInClaims.swift for a Swift structure.
I am now successfully parsing the JWT. The next main step on my server is to actually utilize the different event types in my server to take actions. For me this is going to involve the two account (not email) related actions:
User decided to stop using their Apple Id with your application. And
should be treated as a sign-out by the user. E.g., when a user decides
to disconnect your application from Settings. (From
https://developer.apple.com/videos/play/wwdc2020/10173/)
Also considered a request from user to "delete their app account"
(broader context: "Server to Server Notification Endpoint Sign in with
Apple server to server notifications allow you to receive important
updates about your users and their accounts. Notifications are sent
for each app group when users change mail forwarding preferences,
delete their app account, or permanently delete their Apple ID. Each
group of apps can have one URL, which must be absolute and include the
scheme, host, and path. TLS 1.2 or higher is required to receive
notifications. Learn more.") To see these docs, go to:
developer.apple.com > Account > Certificates, Identifiers & Profiles >
Identifiers > Select your app identifier > Click 'Edit' next to 'Sign
In with Apple' > Server to Server Notification Endpoint
case consentRevoked = "consent-revoked"
User has asked Apple to delete their Apple Id. The user identifier will now no longer be valid.
case accountDelete = "account-delete"
My plan is to take both of these events as equivalent- and delete the user's account on my server. I'm then going to have to consider how to communicate this to my client (iOS app). It will need to know that the user has deleted their account.

How to protect JSON API from being accessed by anyone but my iOS client?

I have an iPhone app that uses a Rails server HTTP API. The API is public at this point - no authorisation is required to get the data.
Currently anyone can go to API's URL and download the data.
http://server.com/mydata
The data is not very sensitive. But I still want to prevent people from easily getting it. What are the ways of doing that? I do not want iOS app users to log in either.
Current solution I have
iPhone app adds a secret token to the HTTP header or query of the request. The data goes over HTTPS.
https://server.com/mydata?secret=my_secret
Is there a better approach?
You could try an approach where the client is only allowed X number of requests per time period (based on IP address or username)
HTTPS is extremely easy to man in the middle on a device you control. You can do SSL cert validation, but there is always someone out there with more time, so best off to handle it server side.
Distribute and use your own SSL certificate.
Apps that transfer sensitive customer data, like credit card and payment information, must be protected from man-in-the middle attacks. The best protection is a mutual authentication scheme, where certificates are exchanged to make sure the app is connected to a trusted server and to make sure the server is connected to a trusted app.
Then only individuals (who have presumably installed your application) have access. If someone digs through the code and gets the public certificate then they can impersonate the client; but at that point they win anyway and two-factor authentication should be explored.

WebAPI + Azure WebSite + Client WebSite + SSL - how many certs do I need?

I have a WebAPI solution hosted in an Azure Web Site (appnameapi.azurewebsites.net) that has some endpoints exposed to regular http right now.
I also have a client application hosted in a separate Web Site under appname.azurewebsites.net.
I purchased appname.com from hover and am forwarding appname.com to appname.azurewebsites.net with masking. The client application makes requests to appnameapi.azurewebsites.net right now, but not encrypted.
My goal is to get SSL working on the web client so that users see SSL in the browser bar, and so that anything that goes from the client to the api endpoints is encrypted.
I went to rapidSSL and purchased a certificate for appname.com. Now I'm not sure if I need to put this in my WebAPI web site, or my client web site. I've found some documentation on setting up SSL in Azure but nothing that's given me a good grasp of what needs to be done in this scenario.
What's the next step? Do I need one cert per site, and if not, where does the single cert go?
You client web site is appname.azurewebsites.net. You have appname.com mapped to this. Your SSL certificate is for this domain. So, you will need to put the certificate with the client app. As an end user, if I go to appname.com, the certificate your application will present to my browser will be the one you purchased for appname.com. This is for the pages rendered by the client web application.
Now, as the browser renders the page from the client web application, say it needs to make jQuery AJAX calls to your web API site appnameapi.azurewebsites.net. You can use a domain name for this one as well, some thing like api.appname.com but regardless, this will be a cross-origin call, BTW. If this call is also through HTTPS, then for this case also, a valid cert must be presented to the browser. Assuming you have api.appname.com which is a sub-domain of appname.com, you can use the same certificate you bought from rapidSSL with web API site as well provided it is a wild-card cert, which is obviously more expensive. Otherwise, you will need one more certificate for the web api site (or the domain name if you plan to use one for API) and install that new cert in the api app.

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.

Resources