Implement basic Authentication for rest api using Jersey 2 - java

I have exposed some rest api using Jersey 2(Tomcat server) and successfully implemented Basic authentication(only needed authentication stuff not authorization) using ContainerRequestFilter filter as below
public class AuthFilter implements ContainerRequestFilter{
#Context
HttpServletRequest request;
#Override
public void filter(ContainerRequestContext context) {
............................
//getting username/password authorization header and validating
When I told the same to my Lead, he said don't use filters as every time your rest api is hit, this filter will get invoked.Therefore, implement basic authentication security at container level.I am using Tomcat server.
In web.xml, this is defined
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
Is the above he is referring to?
Can anyone please guide me how to implement the way my lead is saying?

The documentation gives you examples on how to configure this via web.xml. You'll need to configure this using a login-config that belongs to a realm. The web container then takes care of securing resources based on URL patterns.
Note that the data is sent in plain text (in encoded form) via a HTTP header, so you'll need to think of ways to ensure that is not snooped on (like HTTPS).
Whether you check this header on a filter or on the container does not relieve you of the overhead required for making the check (which is probably negligible, but I've never profiled this area of the code to quote numbers).

Related

Get the OAuth2/OIDC access token with every request in Spring

I'm trying to enable multi-tenancy for a previously single-user system. The application used to run on a local server and had a relatively simple frontend baked in.
Now I want to allow multiple users to simultaneously use it in a cloud environment. I went ahead and implemented Auth2 with OIDC and PKCE to redirect users to an external Auth Provider. What I want now is that for every request, the user sends his Access token with the request in order for me to decide what data to provide with the answer.
I could not figure out how to obtain that data, as it seems that the spring framework (by default) only sends the ID token with the request. I suspect the fact that my software would simultaneously be the client and the resource server has something to do with it.
This is my first question, so I'm very happy to modify or extend my question if I've forgotten anything.
What I've tried to far:
I've used Postman to verify that the three tokens, ID token, refresh token and access token are issued correctly and can be retrieved with my credentials.
I tried getting the access token from the request itself. Any parameters (like #AuthenticationPrincipal OidcUser oidcUser) in the controller that include the token, however, are only showing the ID token and not the access token.
Getting the token via the OAuth2AuthorizedClientService does not work either, same problem, as I can only get the ID token, but not the access token.
Update #1, 13.12.2022/11:40: I am using PingOne by PingIdentity as the authentication provider and the following dependencies are or might be related or helpful to this matter:
spring-boot-starter-web
spring-boot-starter-security
spring-boot-starter-thymeleaf
spring-boot-starter-web-services
spring-boot-starter-oauth2-resource-server
Split your security in two with http.securityMatcher in the first picked SecurityFilterChain bean to restrict the endpoints it applies to (use #Order to control that), and configure WebClient to add access-token as Authorization header to its requests to resource-server (REST endpoints). This is demoed in the tutorial I already linked in my comment to your question. This tutorial matches exactly what you are asking, the fact that it uses Keycloak instead of PingOne as authorization server is a detail (solved by editing a property or two).
Resource-server security filter-chain
As reminder, a resource-server is a REST API secured with OAuth2. All end-points of #RestController and #Controller with #ResponseBody should be secured with a SecurityFilterChain containing http.oauth2ResourceServer(). The easiest way to configure a resource-server with Spring is using spring-boot-starter-oauth2-resource-server (or one of the very thin wrappers I wrote around it which enable to configure it from properties with 0 java conf)
By default with such filter-chains, Spring populates the security-context with a sub-class of AbstractOAuth2TokenAuthenticationToken<?>. You can retrieve the access-token from it. At least 2 options to access this Authentication instance:
SecurityContextHolder.getContext().getAuthentication()
have Spring auto-magically inject AbstractOAuth2TokenAuthenticationToken<?> auth as controller method parameter
You can also have the original authorization header injected as controller method parameter with #RequestHeader(value = HttpHeaders.AUTHORIZATION) String authorizationHeader.
I expose various ways to configure resource-servers security filter-chain in this tutorials.
Client security filter-chain
End-points of #Controller returning a template name and secured with OAuth2 are clients. The easiest way to configure a Spring client is with spring-boot-starter-oauth2-client and http.oauth2Login().
Note that in this configuration, the request between the browser and the Spring client is not OAuth2 (it is most frequently secured with a session cookie, not a Bearer access-token in Authorization header). Only requests sent by the Spring client (on the server) to resource-servers are secured with OAuth2.
With client security filter-chain, security-context is populated with OAuth2AuthenticationToken which, on purpose, exposes ID-token and not access-token. As reminder, ID tokens are holding user identity data and are intended to be used by clients when access-tokens audience is resource-servers and is designed for access-control. Clients should consider access-tokens as black box and use it only to authorize their requests to resource-servers (set Bearer Authorization header).
You can get the access-token string from OAuth2AuthorizedClient: authorizedClient.getAccessToken().getTokenValue(), which is itself retrieved from the OAuth2AuthorizedClientService you can auto-wire in your controllers: authorizedClientService.loadAuthorizedClient("your-client-id", auth.getName()) (auth being the OAuth2AuthenticationToken instance retrieved from security-context via SecurityContextHolder or controller method parameter injection)
If you need to authorize a WebClient request from client to resource-server, you can do simpler than retrieve access token and position authorization header: do as in the UiController of the tutorial already linked in my comment to your question:
final var authorizedClient = authorizedClientService.loadAuthorizedClient("spring-addons-public", auth.getName());
final var response = webClient.get().uri("http://localhost:8080/api/greet")
.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))
...
With WebClient configured as follow:
#Bean
WebClient webClient(ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientService authorizedClientService) {
var oauth = new ServletOAuth2AuthorizedClientExchangeFilterFunction(
new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository,
authorizedClientService));
oauth.setDefaultClientRegistrationId("spring-addons-public");
return WebClient.builder().apply(oauth.oauth2Configuration()).build();
}
Thanks to those who tried to help me, but eventually I figured it out myself.
I extended my Controllers by two attributes: OAuth2AuthenticationToken authentication and HttpServletRequest request.
Also, I #Autowired in the OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepository.
This then allows the following call returning the value of the accessToken:
(oAuth2AuthorizedClientRepository.loadAuthorizedClient(myClientRegistrationId, authentication, request)).client.getAccessToken().getTokenValue();.
After that, it's just parsing the token and retrieving the values using JWTParser.parse() and provided methods from the JWT-result.
Personal note: Don't do the parsing and retrieving value parts in your controller to keep any more complex logic out of it.
I hope this helps somebody!

Securing REST services in Jersey

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/

When is a Jersey Rest service actually started/closed?

So I have a Jersey REST service on a Tomcat server that takes client requests, processes them with an Entity Manager to get data from a database, then sends back a response to the client. So my question is, when is my REST class (containing the URL paths and such) created/destroyed? Is it created fresh with every new AJAX request or does it stay running and open on the server indefinitely?
Thanks!
Jersey basically runs as a single servlet that handles all of the requests. When a request is received, the URI+media types is matched (by the servlet implementation) against all the paths you have defined in your various Jersey-annotated classes. If a match is found, Jersey instantiates the relevant class and invokes the proper method and does all the appropriate magic based on annotations and return types.
The one Jersey servlet gets started once. Your Jersey-annotated class gets a new instance for each request that it handles. I usually have a bunch of #Context-annotated parameters for my constructor, so that I have all the relevant context for the request (request, URI, headers, security context, etc.) available to my API implementations.

Authentication in multiple methods with JAX-RS/Jersey - How to keep it clean?

I'm working on a web service written in Java using JAX-RS/Jersey and Spring.
The WS authenticates a user when he or she logs in and returns an access key which has a limited lifespan. Most of the available methods require a valid "access key + userId" combination.
For all of the available methods I am using #FormParam to retrieve POST parameters, and the response is in JSON. What I am trying to figure out is how to avoid having to type out
#FormParam("userId") final Integer userId,
#FormParam("accessKey") final String accessKey
and manually invoke my authentication method for each of the probably 20 methods I will have that require authentication. This is especially cumbersome because I will then also have to add code to handle e.g. expired access keys.
Is there another way to go about retrieving the two parameters above and performing authentication so I can keep my methods clean?
Since you're using Jersey, you can use servlet-filter-like APIs to DRY up your code. Check out:
Equivalent of Servlet Filter for Jersey / JAX-RS / REST resources?
How does one intercept a request during the Jersey lifecycle?
ContainerRequestFilter

SOAP Header using JAX-WS(Server Side)

The requirement is to include security attributes in header of Soap Message in every WebService request. One way to include is:
#WebService
#SOAPBinding
(
style = javax.jws.soap.SOAPBinding.Style.DOCUMENT,
use = javax.jws.soap.SOAPBinding.Use.LITERAL
)
public interface UserService
{
#WebMethod
public AuthenticateResponse authenticateUser(AuthenticateRequest request, #webParam(header=true) ApplicationCredential appcredential);
#WebMethod
public UserDetailResponse getUserDetail(UserDetailRequest request, #webParam(header=true) ApplicationCredential appcredential);
}
But, with the above mentioned approach, I need to include ApplicationCredential at every operation. It doesn't look good. I am looking for if we can include this ApplicationCredential class in BaseRequest Class and mention there that it is going to be a part of Soap Header(through some annotaion), that would be really helpful. For ex:
public class BaseRequest
{
#SomeAnnotation which states that Appcedential is a part of Soap Header
ApplicationCredential appcredential;
}
So far, I am unable to find any way to do this. Any help would be highly appreciated.
I know it's a while ago you asked that question, and you may have found out yourself already, but I answer to it anyway: Try to avoid to create your own authentication handshake for Web Services - instead use either Web Service message level security if your server and client provide it, or just use transport level security, e.g. Basic Authentication and/or SPNEGO.
The selection of the authentication mechanism depends mainly on your context: If you provide the service within a company network, use whatever is provided there, or Basic Authentication if no central authentication infrastructure is in place. If you provide your Web Service to the Internet, the easiest way to do authentication is again Basic Authentication (via SSL), but that again depends on the kind of service consumers you envision.
This answer creates more questions, I guess, sorry about that. My main point is that you should not try to reinvent the wheel again =:-)

Categories

Resources