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.
Related
I make use of the class org.springframework.security.jwt.JwtHelper from org.springframework.security:spring-security-jwt:1.1.0.RELEASE for decoding JWT tokens, e.g.
Jwt jwt = JwtHelper.decode(accessToken);
String claims = jwt.getClaims();
The above classes are deprecated and the deprecation comment points to Spring Security OAuth 2.0 Migration Guide.
This guide does not talk about any replacement for JwtHelper.
I found the class JwtDecoders which creates a JwtDecoder in the new spring-security-oauth2 project. But JwtDecoders requires an issuer to be passed.
Since I do not wish to verify the token, is there a simple alternative available? Otherwise I can split on . and base64-decode the token, and use any JSON library to parse.
The replacement used in Spring Security is nimbus-jose-jwt. If you don't use Spring Boot, you have to choose a version otherwise Spring Boot will choose one for you.
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
</dependency>
You can just use it like this:
import com.nimbusds.jwt.JWTParser;
....
JWT jwt = JWTParser.parse(accessToken)
Header = jwt.getHeader();
JWTClaimsSet jwtClaimSet = jwt.getJWTClaimsSet();
This worked fine for me without any new dependency
Jws<Claims> claimsJws = Jwts.parserBuilder().setSigningKey(Keys.hmacShaKeyFor("secretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecret".getBytes()))
.build().parseClaimsJws(token);
String username = claimsJws.getBody().getSubject();
Authentication authentication = new UsernamePasswordAuthenticationToken(username,null, null);
SecurityContextHolder.getContext().setAuthentication(authentication);
Do not skip token verification! Failure to verify the token properly will result in an insecure app.
It is very important that you check the issuer (iss claim) of the token and verify it is correct and that it is supposed to be accepted by your application. Only accept tokens from issuers that have the authority to grant access tokens for your app.
Also verify the token is intended for your app (check aud claim): you don't want users misusing tokens intended for other apps (e.g., if user has token with all the right claims, but with aud claim set to another app; that shouldn't be a valid token for you).
Now, make certain to check the signature of the token to verify it is actually signed by the issuer and it is not a bogus token: you can find the issuer's public keys by contacting the issuer. If you don't get the public key directly from the issuer, and you don't verify the signature of the incoming token properly, a malicious user will be able to forge a seemingly-valid token that your app will accept, and your app will be at risk of leaking catastrophic amounts of data.
The last step is to check validity (is it expired?) and to then to check for whatever other claims or scopes your app expects and requires.
If you wish to avoid an additional dependency (e.g. nimbus-jose-jwt), feel free to fork or copy this small utility class: JwtUtils.java
When we configure Server-to-Server Notifications, we Specify our secure server's URL in App Store Connect and the apple server communicates on that URL. but is there a way to authenticate this request?
It is not safe to keep url open without authentication
in case of PlayStore we can use GOOGLE_DEVELOPER_API_KEYFILE_JSON for authentication, but how to do this for iOS server-server notification?
As the comments have already clarified that there is no built in way.
So, here is my work around of this problem.
Apple sends password in the notification which is App secret key which ideal should only be known by API and Apple.
And to verify receipts coming from the App this password must already be stored somewhere (configuration?) in the API.
So I suggest to check whether the password in request matches with the one stored in our API?
If yes then this is a valid request.
If not then it may be sent by a hacker.
My only concern is that does this App shared secret key aka password change? by Apple or developer? If not than this is the solution.
One way to do it is to use Basic auth. As you cannot specify a header you can use the url format: https://username:password#SERVER_ENDPOINT. This will automatically encode the username:password and construct a basic auth header with the encoded string.
Source:
https://en.wikipedia.org/wiki/Basic_access_authentication
I am a developing an Rest API in spring boot and it has to be consumed by an IOS Application.I have implemented JWT token with Oaut 2.0 as security and my web server is having SSL.So the call will be made by
https://server:port//dataurl
with the header as the token. My question is since My web Server is having SSL so the connection channel will be secured (because the token should be passed in a secure channel ) or do the client side (IOS App) should also have to implement SSL Certificate. I am a having a little confusion about how the SSL channel communication. Any help is appreciated.
This link has a nice graph about how SSL works.
https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_7.1.0/com.ibm.mq.doc/sy10660_.htm
You can have a look of AFNetworking, no matter you want to use it or not.
In particular, have a look of AFSecurityPolicy.h and how it is used in AFURLSessionManager.m. That could be a good start point.
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.
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).