How can the resource server identify the resource owner using token in oauth2? - rest

The typical scenario I am looking into is:
User1 provides proper credentials to the front-end rest client (grant type: password) and the client gets the token in return.
The client sends the token and accesses the resources owned by User1.
In my scenario, once the client has the access token for user1, I want the client to have access limited to User1's resources only.
Consider that the client accesses the URI /users/1/books. The response will contain all the books associated with User1. The main problem is that if the client accesses the URL /users/2/books with User1's token, it gets the list of all the books for User2 which shouldn't be allowed.
How can I limit the scope to the user whose credentials were used to obtain the token?
How can I map the token to a specific user in my resource server?
I am using Spring/Java. But any general theory will also help.

After a lot of debugging, I got the answer.
Spring security 1.4
Token store: InMemoryTokenStore()
In ResourceServerConfiguration, configure HttpSecurity.
#Override
public void configure(final HttpSecurity http) throws Exception {
// #formatter:off
http.authorizeRequests().
// antMatchers("/oauth/token").permitAll().
antMatchers("/api/users/{userId}").access("#webSecurity.checkUserId(authentication,#userId)")
.anyRequest().authenticated().and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().csrf().disable();
// #formatter:on
}
Create a class WebSecurity and provide the implementation.
public class WebSecurity {
public boolean checkUserId(Authentication auth, int id) {
return ((UmUser)auth.getPrincipal()).getId() == id;
}
}
Resources:
http://docs.spring.io/spring-security/site/docs/current/reference/html/el-access.html#el-access-web-path-variables
http://www.baeldung.com/get-user-in-spring-security
I had to debug a lot as I was using JwtTokenStore. This returned the Principal as a String and not the instance of UserDetails as I was expecting.
Once I switched to InMemoryTokenStore, I got the expected results. This was not a problem for me as I had the choice, but I would still like to know how to achieve it with the JWT.

Related

How do I load a "user" in a micronaut backend when JWT is provided

I have a Micronaut microservice that handles authentication via JsonWebTokens (JWT) from this guide.
Now I'd like to extend this code. The users in my app have some extra attributes such as email, adress, teamId etc. I have all users in the database.
How do I know in the backend controller method which user corresponds to the JWT that is sent by the client?
The guide contains this example code for the Micronaut REST controller:
#Secured(SecurityRule.IS_AUTHENTICATED)
#Controller
public class HomeController {
#Produces(MediaType.TEXT_PLAIN)
#Get
public String index(Principal principal) {
return principal.getName();
}
}
I know that I can get the name of the principal, ie. the username from the HttpRequest. But how do I get my additional attributes?
(Maybe I misunderstand JWT a bit???)
Are these JWT "claims" ?
Do I need to load the corresponding user by username from my DB table?
How can I verify that the sent username is actually valid?
edit Describing my usecase in more detail:
Security requirements of my use case
Do not expose valid information to the client
Validate everything the client (a mobile app) sends via REST
Authentication Flow
default oauth2 flow with JWTs:
Precondition: User is already registerd. Username, hash(password) and furhter attributes (email, adress, teamId, ..) are known on the backend.
Client POSTs username and password to /login endpoint
Client receives JWT in return, signed with server secret
On every future request the client sends this JWT as bearer in the Http header.
Backend validates JWT <==== this is what I want to know how to do this in Micronaut.
Questions
How to validate that the JWT is valid?
How to and where in which Java class should I fetch additional information for that user (the additional attributes). What ID should I use to fetch this information. The "sub" or "name" from the decoded JWT?
How do I load a “user” in a micronaut backend when JWT is provided?
I am reading this as you plan to load some kind of User object your database and access it in the controller.
If this is the case you need to hook into the place where Authentication instance is created to read the "sub" (username) of the token and then load it from the database.
How to extend authentication attributes with more details ?
By default for JWT authentication is created using JwtAuthenticationFactory and going more concrete default implementation is DefaultJwtAuthenticationFactory. If you plan to load more claims this could be done by replacing it and creating extended JWTClaimsSet or your own implementation of Authentication interface.
How do I access jwt claims ?
You need to check SecurityService -> getAuthentication() ->getAttributes(), it returns a map of security attributes which represent your token serialised as a map.
How to validate that the JWT is valid?
There is a basic validation rules checking the token is not expired and properly signed, all the rest validations especially for custom claims and validating agains a third parties sources have to be done on your own.
If you plan to validate your custom claims, I have already open source a project in this scope, please have a look.
https://github.com/traycho/micronaut-security-attributes
How to extend existing token with extra claims during its issuing ?
It is required to create your own claims generator extending JWTClaimsSetGenerator
#Singleton
#Replaces(JWTClaimsSetGenerator)
class CustomJWTClaimsSetGenerator extends JWTClaimsSetGenerator {
CustomJWTClaimsSetGenerator(TokenConfiguration tokenConfiguration, #Nullable JwtIdGenerator jwtIdGenerator, #Nullable ClaimsAudienceProvider claimsAudienceProvider) {
super(tokenConfiguration, jwtIdGenerator, claimsAudienceProvider)
}
protected void populateWithUserDetails(JWTClaimsSet.Builder builder, UserDetails userDetails) {
super.populateWithUserDetails(builder, userDetails)
// You your custom claims here
builder.claim('email', userDetails.getAttributes().get("email"));
}
}
How do I access jwt claims ?
If you want to access them from the rest handler just add io.micronaut.security.authentication.Authentication as an additional parameter in the handling method. Example
#Get("/{fooId}")
#Secured(SecurityRule.IS_AUTHENTICATED)
public HttpResponse<Foo> getFoo(long fooId, Authentication authentication) {
...
}
I found a solution. The UserDetails.attributes are serialized into the JWT. And they can easily be set in my CustomAuthenticationProviderclass:
#Singleton
#Slf4j
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Override
public Publisher<AuthenticationResponse> authenticate(
#Nullable HttpRequest<?> httpRequest,
AuthenticationRequest<?, ?> authenticationRequest)
{
// ... autenticate the request here ...
// eg. via BasicAuth or Oauth 2.0 OneTimeToken
// then if valid:
return Flowable.create(emitter -> {
UserDetails userDetails = new UserDetails("sherlock", Collections.emptyList(), "sherlock#micronaut.example");
// These attributes will be serialized as custom claims in the JWT
Map attrs = CollectionUtils.mapOf("email", email, "teamId", teamId)
userDetails.setAttributes(attrs);
emitter.onNext(userDetails);
emitter.onComplete();
}, BackpressureStrategy.ERROR);
}
}
And some more pitfalls when validating the JWT in the backend
A JWT in Micronaut MUST contain a "sub" claim. The JWT spec does not require this, but Micronaut does. The value of the "sub" claim will become the username of the created UserDetails object.
If you want to load addition attributes into these UserDetails when the JWT is validated in the backend, then you can do this by implementing a TokenValidator. But (another pitfal) then you must set its ORDER to a value larger than micronaut's JwtTokenValidator. Your order must be > 0 otherwise your TokenValidator will not be called at all.

How to Identify caller when grant_type is client_credentials?

I have ASP.NET Web application and I am using IdentityServer3 for user authentication. Our customers login to web application using userid/password.
Now I have one more Web API and some of our customers need to call this web api from their applications. (server to server communication). So based on this, in identityserver i did the following
1> Created a new scope name api
2> Created a new client for Web API and configured with allowed scope api and offline_access
3> Set flow to ClientCredentials
4> Set AccessTokenType to Jwt
5> For each customer i created different secret key
Now our customers can get access token at connect/token endpoint and then make call to API using the access token. The API validates the token with IdentityServer, and then returns the result. All good till here.
However, in API project i also need to identify the customer aka caller. Based on customer i need to do some logic
public class ResourcesController : ApiController
{
public IHttpActionResult Get()
{
var caller = User as ClaimsPrincipal;
// need to identify caller here
return Json(new
{
message = "OK",
});
}
}
(One option i can think of is taking customer id is as part of API url. Something like http://api.domain.com/v1/customerid/resources)
Is there anyway to make a use of IdentityServer to identify customer?
I've actually had a similar need some time ago. For the simplest solution, you should be able to assign a custom claim to each of the Identity Server client that you have created for your customers.
AlwaysSendClientClaims = true,
Claims = new List<Claim>()
{
new Claim("CustomerId", "0121021")
}
These client claims will then be included in the access token and therefore available to you in your backend.
public class ResourcesController : ApiController
{
public IHttpActionResult Get()
{
var caller = User as ClaimsPrincipal;
// need to identify caller here
var customerId = caller?.Claims.Where(p => p.Type.Equals("CustomerId")).First().Value;
// need to identify caller here
return Json(new
{
message = "OK",
});
}
}

How to implement user login in Jersey API

I am implementing RESTful API using javax.ws.rs. The response of the calls that I need to implement requires knowing which user is logged in currently. I tried making a separate call for the user login:
api.myapi.co.uk/authenticate?username=xxxx&password=xxxx
where I basically save the user information is a global variable
and then tried to make another call to retrieve information from the database based on the user that has been saved earlier but I find the value as null during the second call. What am I missing? Is my approach wrong? do you have any suggestions please?
Your code probably looks like this, right?
#Path("/")
public class MyResource{
private String username;
private String password;
#Path("authenticate")
public String authenticate(#QueryParam("username") username, #QueryParam("password") password) {
if(!username.equals("zainab") || !password.equals("letmein"))
return "Incorrect username or password";
this.username=username;
this.password=password;
return "Sucessfully authenticated";
}
#Path("secret")
public String secret() {
if(username == null || password == null)
return "Sorry, not authorized";
return "You are authorized: "+username;
}
}
If so, the problem is that JAX-RS creates a new Resource object for each request. A request to "/secret" then uses a new instance of MyResource, which has username and password as null.
Then, you might think, I'll just make it static! Then, your resource can't handle concurrent requests. What if Person A calls "/authenticate", then "/secret". Then Person B calls "/secret" without authenticating. He can then access "/secret" without authenticating!
Anyways, this violates the idea of RESTful services. The S in RESTful stands for "Stateless". This means that the server should store no state per client, and possibly give the user a token to pass with concurrent requests.
One possibility is to accept the username and password for every request to secret ("/secret?username=zainab&password=letmein"). Or you could implement token-based authentication, where the user calls "/authenticate" to get a token, and they pass that token on all later requests. Here is a link to help with that.
Also, note that username and password is usually not send in the URL as a query param, but instead in the Authorization HTTP header as such Authorization: base64encode("USERNAME:PASSWORD")

Storing User Credientials in the Bayeux Server

I'd like to store the clients UserName and SessionId when a client subscribes to a particular channel. When i override canHandshake() i can get the user credentials using the following:
userName = (String) authentication.get("userName");
sessionId = (String) authentication.get("sessionId");
Just wondering how i can store these credentials and later retrieve them? I've had a look at the authentication documentation here and it just mentions linking the authentication data to the session. Is this the Bayeux Server side session??
Thanks
The "linking" can be done in several ways.
You can link this information in an external map via:
#Override
public boolean canHandshake(BayeuxServer server, ServerSession session, ServerMessage message)
{
...
Map<String, Object> authentication = ...;
map.put((String)authentication.get("userName"), session);
...
}
where the map can be a java.util.ConcurrentHashMap<String, ServerSession> field in the security policy itself, or in another object such as a user service.
For simpler use cases, the userName can be linked directly to the session in this way:
session.setAttribute("userName", authentication.get("userName"));
Or you can use both techniques.
This is the updated link for the authentication how-to, and you can find the latest comprehensive CometD documentation at http://docs.cometd.org.

Jira 5.2 Seraph SSO Login behind reverse proxy

Since a few days I'm trying to enable SSO for Jira 5.2 and figured out, that the help page from Jira is outdated.
Each example uses an old version of atlassian-seraph (Jira 5.2 uses 2.6.0).
Goal:
I want to get automatically logged in into Jira if I'm logged in into Webseal (reverse proxy).
Background:
Jira is behind a reverse proxy (see picture).
This proxy authentificatates the user and holds the session.
If I'm logged in I want to be logged in in Jira, too
The only information provided is the user name
Question:
How to write a custom login module that reads the username from http_header and authentificates the user?
Links:
https://confluence.atlassian.com/display/DEV/Single+Sign-on+Integration+with+JIRA+and+Confluence
http://docs.atlassian.com/atlassian-seraph/latest/sso.html
https://answers.atlassian.com/questions/23245/how-to-integrate-jira-with-my-company-s-sso
In the end i figured it out by myself:
You need a custom authenticator
public class MyCustomAuthenticator extends DefaultAuthenticator {
protected boolean authenticate(Principal user, String password)
throws AuthenticatorException {
return true;
}
protected Principal getUser(String username) {
return getCrowdService().getUser(username);
}
private CrowdService getCrowdService() {
return (CrowdService)ComponentManager.getComponent(CrowdService.class);
}
}
Add the MyCustomAuthenticator to seraph-config.xml
<authenticator class="com.company.jira.MyCustomAuthenticator"/>
Write a Custom Filter to set the user name from http-header
public class CustomFilter extends PasswordBasedLoginFilter {
#Override
protected UserPasswordPair extractUserPasswordPair(
HttpServletRequest request) {
String username = request.getHeader("iv-header");
if (username != null && username.trim().length() != 0) {
return new PasswordBasedLoginFilter.UserPasswordPair(
username, "DUMMY", false);
}
return null;
}
}
Replace the filter within the web.xml
<filter>
<filter-name>login</filter-name>
<filter-class>com.company.jira.CustomFilter</filter-class>
</filter>
These jar's are needed for Jira 5.2
embedded-crowd-api-2.6.2
jira-core-5.2.1
atlassian-seraph-2.6.0
I am not familiar with Jira authentication, but I do understand well the SiteMinder/ WebSeal authentication.
Both systems authenticate user and send the user name in an HTTP header.
The name of HTTP header can be configured. Also, they can send additional user properties, like the user email in the additional HTTP headers.
TO authenticate a user behind SiteMinder/ WebSeal it is just required to take the HTTP header and to create an application session using the user name from the header.
You definitely can solve it in Jira. You have 2 options:
To use already created SiteMinder authenticator:
https://confluence.atlassian.com/display/DEV/SiteMinder+Custom+Seraph+Authenticator+for+Confluence
The problem that I did not find how to configure the HTTP header name for the user name header. It assumes that the header name is uid
You need to configure the header uid in WebSeal or try to obtain sources and make the header name configurable.
Implement your own authenticator according to your link:
http://docs.atlassian.com/atlassian-seraph/latest/sso.html
Obtain the user name using the code
httpServletRequest.getHeader(userNameHeaderName);