I have found numerous academic answers to this question, but I would like one from practitioners in the field.
Background
I would like to create a Java-based RESTful API, using the Grails framework for a variety of mobile clients (iOS and Android) to access protected resources through my service. I require authentication on certain requests, and I already have SSL setup over the wire (so all requests occur over https). My web API will eventually be exposed as a service to other web applications.
Problem
What authentication method do people recommend for a web service that is to be consumed by mobile devices, and eventually other web applications?
These are what I see as my choices. Can you tell me when would be appropriate use cases for each one?
I can do HTTP Basic authentication
I can do HTTP Digest authentication
I can implement OAuth authentication (1.0 or 2.0)?
I can pass the credentials as parameters in my request
I can use an authentication method above, and then pass a delegate/token around for authentication
I can implement my own custom HTTP authentication headers
I can use cookies and pass those to the server on each request
Other...?
Need
If you have one leaning one way or another, I'd like to know why you would choose that method. Better yet, if you're doing this in Grails, I'm very interested.
I already know...
I've already read through the excellent answers here and Richardson and Ruby's book, Restful Web Services.
REST is stateless protocol, thus using "work sessions" - I mean login/work/(auto)logout concept is somewhat questionable. Sending credentials as a parameter with each request seems to be the most often used method due to its simplicity. Just keep in mind that
1) The api url must be SSL only - it makes sense to use a dedicated domain AND ip address, e.g. api.example.com and configure your web server to handle SSL only for this address and domain. Just to avoid accidental disclosure of credentials.
2) Avoid using login/password with the request if possible, use "API key" (a shared secret) instead. You always may use "API key" instead of login/password if authentication is all you need, that is you do not need authorization (all users share the same permissions) and no need for logging (who did what).
3) Rather than sending the API key with each request, it is better to "sign" the request using shared secret and supply the signature. With the key long enough you may use this technique over plain unencrypted http.
=== responding to a comment ===
If you need authorization, just use basic authentication:
HTTPBuilder builder = new HTTPBuilder("https://api.example.com/v1/foo/bar")
builder.auth.basic(login, password)
builder.headers.put('Accept', 'application/json')
def result = builder.request(POST, JSON) { req ->
body = [
....
]
response.'201' = { resp, json ->
....
}
response.success = { resp, json ->
....
}
response.failure = { resp ->
log.error "failure, ${resp.statusLine}"
}
}
Related
According to official spring documentation:
WebSockets reuse the same authentication information that is found in
the HTTP request when the WebSocket connection was made. This means
that the Principal on the HttpServletRequest will be handed off to
WebSockets. If you are using Spring Security, the Principal on the
HttpServletRequest is overridden automatically.
More concretely, to ensure a user has authenticated to your WebSocket
application, all that is necessary is to ensure that you setup Spring
Security to authenticate your HTTP based web application.
If I understood it correctly, this means that WebSocket is using the same channel for communication since the handshake, and thus the authentication should be made on the first connection.
However nowhere is stated how to actually authenticate the handshake in a standard secure way. As far as I am aware HTTP doesn't send an Authentication header while upgrading to the WebSockets so how it is done?
Do I really need to send authentication token in connection query, e.g
localhost:8080/ws?Auth=...
and leave the security to HTTPS
Or do I need to authenticate the WebSocket after the connection is made e.g create my own handshake?
Is there any proper formal way to do it? I am using RAW websockets.
Thanks for the ideas/help.
I personally use STOMP but with STOMP (basically a framework for communications on raw WebSocks), the session cookie (from spring security) is send with any message down the socket.
You can use the StompAccessorHeader like:
#MessageMapping("/agents/start")
public void start(StompHeaderAccessor stompHeaderAccessor) {
log.info("Subscriber Start! {}-{}", stompHeaderAccessor.getUser() != null ? stompHeaderAccessor.getUser().getName() : "ANON", stompHeaderAccessor.getSessionId());
mysessionstore.addSessionId(stompHeaderAccessor.getSessionId());
}
With not using the STOMP framework there may be a way to read the SessionCookie sent per request on the raw socket?
I am not 100% sure but I am guessing you are using the TextWebSocketHandler implmentation with the:
#Override
protected void handleTextMessage(WebSocketSession session, TextMessage textMessage)
I can see in the source code for WebSocketSession you should be able to get your principal authenticate user there:
https://github.com/spring-projects/spring-framework/blob/0de2833894c24c1e70bde991bad171435c6ecac2/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketSession.java#L37
So you authenticate like normal REST like POST "/login" and then that session should be valid for the websockets as well.
You may be able to auth via the socket? You'd have to like make your own socket endpoint to take their credentials and do SecurityContextHolder.getContext().setAuthentication(myAuthUserToken) but maybe that will then pass a session cookie back? You'd have to test this ofc as I am unsure if it would work shrug.
I personally then make a "store" (a singleton or redis) that holds the user principal and the socketSessionId so I can then match a user to a socket.
You could say store them in a singleton with a HashMap<String,String> userPrincipalNameToSocketSessionId as a crude way store which socket session belongs to which user.
eg.
A bit like
#Override
protected void handleTextMessage(WebSocketSession session, TextMessage textMessage){
MySessionStore.addSessionToMap(session.getPrincipal(),session.getId());
log.info("Added user {} websocket session {} to the store.",session.getPrincipal(),session.getId());
}
public */MySingletonClass*/ MySessionStore{
#Getter
public static volatile HashMap<String,String> userPrinciapalToSocketMap = new HashMap<>();
//Method to add to map here
public synchronized static addToMap(String principalName,String webSocketSessionId){
...Adds to the map.
}
JWT Stateless Auth System wanting a Socket Session?
As far as I can guess with this one...unless there is a lot of overriding and practically forking/extending lots of Spring classes...
You could make a controller:
Http GET => "/websocket-ticket" which would return a signed token with the user's principal/username/id for the UX to then pass as a first message after websocket connect.
The socket handler TextMessageHandler can check the signaute of the token and add it to your HashMap<String,String> principalUserToSessionId store.
The security issue (unlikely but it is there):
An attacker with XSS could snoop that token and hijack that websocket session. Maybe you win on a race condition (i.e. the MITM takes longer and the token is single use...more wonderful implementation...you now also need a "websocket-ticket-consumed" store...).
https://devcenter.heroku.com/articles/websocket-security
I am feeling this is all heading to the X/Y problem.
Why are you using JWTs for Auth?
Maybe this is a strange question (I'm new with Microservices). But I'm looking for some info on how proceed with this. Does not need to be Spring specific, but that's the framework I'm using at the moment.
Example:
Lets say we have two Microservices
a) http://myurlfortesting.com:8085/api/rest/serviceone
b) http://myurlfortesting.com:8090/api/rest/servicetwo
and we have setup Spring Zuul (acting as the API Gateway) with the following rules that forward the incoming calls:
/rest/one -> http://myurlfortesting.com:8085/api/rest/serviceone
/rest/two -> http://myurlfortesting.com:8090/api/rest/servicetwo
The question...
Is there a way to stop users from directly accessing the services mentioned in A and B (only allow the ones that come through the API Gateway)?
Can this be done with Springs Zuul (Acting as a API Gateway) by setting up some extra filters or do we set it up in Microservices endpoints?
Would even like to know if there is a way to not even processing the direct calls on the Microservices endpoints that don't come via the API Gateway.
Maybe this is solved with server specific rules and has nothing to do with Spring?
Many thanks,
/D
Assuming that you have a firewall in place, you could restrict inbound traffic to server to the ports that your Zuul endpoints are exposed on and disallow anyone from accessing the microservices' ports directly.
If you want to avoid going the firewall route, you could force the endpoints to check for a specific HTTP header or something that is set by Zuul prior to forwarding a request, but that would be hacky and easy to circumvent. Based on my past experiences, the "right" way would be to do this via a firewall. Your app should be responsible for dealing with requests. Your firewall should be responsible for deciding who can hit specific endpoints.
Generally, such kind of situation are handled by implementing proper OAuth server wherein only your API gateway will handle the token validation. Any direct call to microservice will not have proper token exchange and hence requests will be aborted.
In case, you have deployed your micro-services on any cloud then you can acheive this by exposing routes to only API gateway.
And yes, firewall blocking, IP whitelisting are some of the other ways in restricting the access to your microservices.
Use a reverse proxy. We use Nginx for the same purpose. Api gateways should always be deployed behind a load balancer in production scenarios to avoid the gateway being a single point of failure(If it is not a managed service like AWS API gateway). Also, the gateway and services are deployed within a VPC and not visible to the public.
Hey I finally find a solution to accept request just from the API Gateway by using microservices architecture, for that you can create a filter, and like Zuul act as a proxy, checking the header 'X-Forwarded-Host', if it doesn't match with the gateway service then return an Unauthorised exception.
public class CustomGatewayFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
String proxyForwardedHostHeader = request.getHeader("X-Forwarded-Host");
if (proxyForwardedHostHeader == null || !proxyForwardedHostHeader.equals(GatewayConstant.getGatewayURL())) {
UnauthorisedException unauthorisedException = new UnauthorisedException("Unauthorized Access",
"Unauthorized Access, you should pass through the API gateway");
byte[] responseToSend = restResponseBytes(unauthorisedException.getErrorResponse());
((HttpServletResponse) response).setHeader("Content-Type", "application/json");
((HttpServletResponse) response).setStatus(401);
response.getOutputStream().write(responseToSend);
return;
}
chain.doFilter(request, response);
}
private byte[] restResponseBytes(ErrorResponse errorResponse) throws IOException {
String serialized = new ObjectMapper().writeValueAsString(errorResponse);
return serialized.getBytes();
}
}
do not forget to add your custom filter in SpringSecurity Configuration
.and().addFilterBefore(new CustomGatewayFilter(), ConcurrentSessionFilter.class);
The right way to do this with AWS API Gateway would be with the recently launched 'VPC Link' integration, which secures the connection between API Gateway and your backend inside your VPC.
https://aws.amazon.com/about-aws/whats-new/2017/11/amazon-api-gateway-supports-endpoint-integrations-with-private-vpcs/
I read through several q&a on stackoverflow for implementing rest authentication. And in one of those questions found a sample code as well.
https://github.com/philipsorst/angular-rest-springsecurity/blob/master/src/main/java/net/dontdrinkandroot/example/angularrestspringsecurity/rest/AuthenticationTokenProcessingFilter.java
Most of the answers talked about having an interceptor and filtering every request based on the auth header (a token and a user id or login id) and comparing it with the ones stored in the database.
I am implementing an Order management system.
And my url looks like http://myapi.com/customers/{customerId}/Orders/{OrderId}
Currently it is http and we're setting up the https soon.
In the URL, I get the customer ID and the order ID. I do a quick look up in the database with the order id and customer id and if it returns some rows, I return a JSON.
Questions I have:
To protect this endpoint, I can have a security interceptor. But every time I'll have to validate the request against the database. What are my alternatives (cache?) to validate or authorize each requests?
This rest end point is consumed by an android app(angular js)client and a website (a php client). For mobile, I should not re generate token each time the user logs in. So I have configured the token expiry to 30 days. However for the website, it is a session token. How should one handle this scenario?
What you need can be solved with Oauth.
Your backend (REST-API) will require authenticated access to your API operations. In turn, your clients/front-end will need to issue authenticated requests when communicating with the backend. This is achieved by sending access tokens.
Although this could seem complex, it will be very useful for you to take a look at Stormpath. We have a quite a straightforward solution for this. Please take a look at Using Stormpath for API Authentication.
As a summary, your solution will look like this:
You will use the Stormpath Java SDK to easily delegate all your user-management needs.
In your front, when the user presses the login button, your front end will send the credentials securely to your backend-end thorough its REST API.
2.1. By the way, Stormpath greatly enhances all the possibilities here. Instead of having your own login page, you can completely delegate the login/register functionality to Stormpath via its IDSite, or you can also delegate it to our Servlet Plugin. Stormpath also supports Google, Facebook, LinkedIn and Github login.
Your backend will then try to authenticate the user against the Stormpath Backend and will return an access token as a result:
/** This code will throw an Exception if the authentication fails */
public void postOAuthToken(HttpServletRequest request, HttpServletResponse response) {
Application application = client.getResource(applicationRestUrl, Application.class);
//Getting the authentication result
AccessTokenResult result = (AccessTokenResult) application.authenticateApiRequest(request);
//Here you can get all the user data stored in Stormpath
Account account = accessTokenResult.getAccount();
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType("application/json");
//Output the json of the Access Token
response.getWriter().print(token.toJson());
response.getWriter().flush();
}
Then, for every authenticated request, your backend will do:
/** This is your (now protected) exposed operation */
public void getOrder(HttpServletRequest request, HttpServletResponse response) {
Application application = client.getResource(applicationRestUrl, Application.class);
OauthAuthenticationResult result = (OauthAuthenticationResult) application.authenticateOauthRequest(request).execute();
System.out.println(result.getApiKey());
System.out.println(result.getAccount());
//Return what you need to return in the response
doGetOrder(request, response);
}
Please take a look here for more information
Hope that helps!
Disclaimer, I am an active Stormpath contributor.
I am very much new to web services. I have exposed some REST services using Jersey 2 in integration with Spring. Now I need to secure those rest services using authentication with username/password. I am told not to use Spring Security.
I have no idea of how to do this. I did search on the net but various links show various implementation and I am unable to decide how to proceed with it.
A common way for authenticating with username and password is to use Basic Authentication. Basically the client needs to send a request header Authorization, with the the header value as Basic Base64Encoded(username:password). So is my username is peeskillet and my password is pass, I, as a client, should set the header as
Authorization: Basic cGVlc2tpbGxldDpwYXNz
In a servlet environment, the container should have support for Basic authentication. You would configure this support on the web.xml. You can see an example in 48.2 Securing Web Applications of the Java EE tutorial. You will also notice in an example
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
That is for SSL support. This is recommended for Basic Authentication.
If you don't want to deal with the hassle of working with security domains and login modules, realm, and such, that would be required to customize the servlet support, or if you're just not in a servlet environment, implementing Basic Auth in a ContainerRequestFilter is really not too difficult.
You can see a complete example of how this could be done at jersey/examples/https-clientserver-grizzly. You should focus on the SecurityFilter
The basic flow in the filter goes something like this
Get the Authorization header. If it doesn't exist, throw an AuthenticationException. In which case the AuthenticationExceptionMapper will send out the header "WWW-Authenticate", "Basic realm=\"" + e.getRealm() + "\", which is part of the Basic Auth protocol
Once we have the header, we parse it just to get the Base64 encoded username:password. Then we decode it, then split it, then separate the user name and password. If any of this process fails, again throw the WebApplicationException that maps to a 400 Bad Request.
Check the username and password. The example source code just checks if the username is user and the password is password, but you will want to use some service in the filter to verify this information. If either of these fail, throw an AuthenticationException
If all goes well, a User is created from the authenticate method, and is injected into an Authorizer (which is a SecurityContext). In JAX-RS, the SecurityContext is normally used for authorization`.
For the authorization, if you want to secure certain areas for certain resources, you can use the #RolesAllowed annotation for your classes or methods. Jersey has support for this annotation, by registering the RolesAllowedDynamicFeature.
What happens under the hood is that the SecurityContext will be obtained from the request. With the example I linked to, you can see the Authorizer, it has an overridden method isUserInRole. This method will be called to check against the value(s) in #RolesAllowed({"ADMIN"}). So when you create the SecurityContext, you should make sure to include on the overridden method, the roles of the user.
For testing, you can simply use a browser. If everything is set up correctly, when you try and access the resource, you should see (in Firefox) a dialog as seen in this post. If you use cURL, you could do
C:/>curl -v -u username:password http://localhost:8080/blah/resource
This will send out a Basic Authenticated request. Because of the -v switch, you should see all the headers involved. If you just want to test with the client API, you can see here how to set it up. In any of the three cases mentioned, the Base64 encoding will be done for you, so you don't have to worry about it.
As for the SSL, you should look into the documentation of your container for information about how to set it up.
So this is really a matter what you would like to achieve. My case was to get this thing running with mobile and a One-Page-App JavaScript.
Basically all you need to do is generate some kind of header that value that will be needed in every consecutive request you client will make.
So you do a endpoint in which you wait for a post with user/password:
#Path("/login")
public class AuthenticationResource {
#POST
#Consumes("application/json")
public Response authenticate(Credentials credential) {
boolean canBeLoggedIn = (...check in your DB or anywher you need to)
if (canBeLoggedIn) {
UUID uuid = UUID.randomUUID();
Token token = new Token();
token.setToken(uuid.toString());
//save your token with associated with user
(...)
return Response.ok(token).type(MediaType.APPLICATION_JSON_TYPE).build();
} else {
return Response.status(Response.Status.UNAUTHORIZED).build();
}
}
}
Now you need to secure resource with need for that token:
#Path("/payment")
#AuthorizedWithToken
public class Payments {
#GET
#Produces("application/json")
public Response sync() {
(...)
}
}
Notice the #AuthorizedWithToken annotation. This annotaation you can create on your own using special meta annotation #NameBinding
#NameBinding
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface AuthorizedWithToken {}
And now for the filter that implements checking of the header:
#AuthorizedWithToken
#Provider
public class XAuthTokenFilter implements ContainerRequestFilter {
private static String X_Auth_Token = "X-Auth-Token";
#Override
public void filter(ContainerRequestContext crc) throws IOException {
String headerValue = crc.getHeaderString(X_Auth_Token);
if (headerValue == null) {
crc.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Missing " + X_Auth_Token + " value").build());
return;
}
if(! TOKEN_FOUND_IN_DB) {
crc.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity("Wrong " + X_Auth_Token + " value").build());
return;
}
}
}
You can create any number of your own annotations checking for various things in the http request and mix them. However you need to pay attention to Priorities but that actually easy thing to find. This method needs using https but that is obvious.
Security comes in two main flavours :
Container Based
application based
the standard way to secure spring applications is to use Spring Security (formerly Acegi).
It would be interesting to know why you're not being allowed to use that.
You could use container based security, but I'm guessing that your use of spring precludes that option too.
Since the choice of Spring is usually to obviate the need for the use of a full J2EE container (Edit : though as pointed out below by others, most ordinary servlet containers do allow you to implement various container based security methods)
This really only leaves you with one option which is to roll your own security.
Your use of Jersey suggests that this might be a REST application.
In which case you really ought to stick with standard HTTP Authentication methods that
comes in the following flavours in reverse order of strength :
BASIC
Digest
Form
Certificate
REST applications are usually supposed to be 'stateless', which essentially rules out form based authentication (because you'd require the use of Session)
leaving you with BASIC, Digest and Certificate.
Your next question is, who am I authenticating. If you can expect to know the username AND the password of the user based on what URL they requested (say if it's one set of credentials for all users) then Digest is the best bet since the password is never sent, only a hash.
If you cannot know the Password (because you ask a third party system to validate it etc.) then you are stuck with BASIC.
But you can enhance the security of BASIC by using SSL, or better yet, combining BASIC with client certificate authentication.
In fact BASIC authentication over HTTPS is the standard technique for securing most REST applications.
You can easily implement a Servlet Filter that looks for the Authentication Header and validates the credentials yourself.
There are many examples of such filters, it's a single self contained class file.
If no credentials are found the filter returns 401 passing a prompt for basic auth in the response headers.
If the credentials are invalid you return 403.
App security is almost an entire career in itself, but I hope this helps.
As the former posts say, you could go with different options, with a varying overhead for implementation. From a practical view, if you're going to start with this and are looking for a comfortable way for a simple implementation, I'd recommend container-based option using BASIC authentication.
If you use tomcat, you can setup a realm, which is relatively simple to implement. You could use JDBCRealm, which gets you a user and password from specified columns in your database, and configure it via server.xml and web.xml.
This will prompt you for credentials automatically, everytime you are trying to access your application. You don't have any application-side implementation to do for that.
What I can tell you now is that you already did most of the 'dirty' job integrating Jersey with Spring. I recommend to you to go an Application-based solution, is it does not tie you to a particular container. Spring Security can be intimidating at first, but then when you tame the beast, you see it was actually a friendly puppy.
The fact is that Spring Security is hugely customizable, just by implementing their interfaces. And there is a lot of documentation and support. Plus, you already have a Spring based application.
As all you seek is guidance, I can provide you with some tutorials. You can take advantage from this blog.
http://www.baeldung.com/rest-with-spring-series/
http://www.baeldung.com/2011/10/31/securing-a-restful-web-service-with-spring-security-3-1-part-3/
Hi I am looking for a way to authenticate users when they make WebSocket connection and simply if they are not authenticated close the connection. I am using Dropwizard framework and Atmosphere for the WebSocket connections. Here is the example that I use.
It would be great if I could use '#Auth' annotation provided by Dropwizard for authentication when the connection is establishing.
How are you exposing this? Is it through a javascript frontend?
You are using the servlet based approach as described in your link and here: https://cvwjensen.wordpress.com/2014/08/02/websockets-in-dropwizard/, and not the jersey atmosphere extension?
If you are using the servlet based approach would recommend using a token-based approach putting the token in the http header an then access this header in the #Ready handler method, like this:
#Ready
public String onReady(final AtmosphereResource resource) {
String AuthHeader = resource.getRequest().getHeader("Authorization");
...DO AUTH HERE
logger.info("Resource {} connected ", resource.uuid());
return "Connect " + resource.uuid();
}
Then, you could also easily close the connection if auth fails. However, depending on your client side implementation, you might want to think about this. If the client auto-reconnects on close, you have a scenario of constant closing and opening of sockets that may cause a resource drain. You could, if auth is not successful, store a private variable that states whether or not this instance is authenticated and just drop sending any messages to or process any messages from it. That would also be a "obscure" way of letting an attacker know that auth failed, it simply is in a limbo state; connected, but not failed or closed. Just not receiving any data. But again, this is specific to your implementation.
Jwt auth is an option, check this out: https://github.com/ToastShaman/dropwizard-auth-jwt. I have, although not made it public, ported this implementation to dropwizard 0.8rc3-SNAPSHOT. If you need this, please let me know and I can post it to github.