Using #RunAs between a servlet and an EJB - authentication

I have the following servlet.
#DeclareRoles("remote-guest")
#RunAs("remote-guest")
public class GuestServlet extends HttpServlet {
#EJB
private Test test;
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
test.guest();
}
}
The servlet is mapped such that only users with role guest can call it.
<servlet>
<servlet-name>guest-servlet</servlet-name>
<servlet-class>test.web.GuestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>guest-servlet</servlet-name>
<url-pattern>/guest</url-pattern>
</servlet-mapping>
<security-role>
<role-name>guest</role-name>
</security-role>
<security-constraint>
<web-resource-collection>
<web-resource-name>guest-resources</web-resource-name>
<url-pattern>/guest</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>guest</role-name>
</auth-constraint>
</security-constraint>
The Test EJB is implemented by the following TestBean class.
#Stateless
#DeclareRoles("remote-guest")
public class TestBean implements Test {
#RolesAllowed("remote-guest")
public void guest() {
System.out.println("TestBean.guest()");
}
}
Problem: when I call the GuestServlet using a user that is mapped only to the guest role, the EJB throws an javax.ejb.EJBAccessException despite of the presence of the #RunAs annotation on the servlet. Should I also map the user to the remote-guest role? If the user has to be mapped both to guest and remote-guest, what's the real purpose of #RunAs?

Yes, a user needs to be mapped to the role. Although it might be a different user from the one that has the "guest" role. As stated in the EJB 3.1 specification 17.3.4.1:
Because the Bean Provider and Application Assembler do not, in
general, know the security environment of the operational
environment, the run-as identity is designated by a logical role-name,
which corresponds to one of the security roles defined by the Bean
Provider or Application Assembler in the metadata annotations or
deployment descriptor.
The Deployer then assigns a security principal defined in the
operational environment to be used as the principal for the run-as
identity. The security principal assigned by the Deployer should be a
principal that has been assigned to the security role specified by
RunAs annotation or by the role-name element of the run-as deployment
descriptor element.
And oracle jee6 tutorial:
http://docs.oracle.com/cd/E19226-01/820-7627/bnbyr/index.html
The role of #RunAs is to allow communication between 2 containers with different sets of roles and users (as in the link above) or to provide security identity for components that don't have security contexts: for example a Message Driven Bean needs to call another bean that requires the caller to have some roles.

Related

How to Use AWS Cognito without using API Gateway for Custom Spring REST API Authorization

Scenario :
I will have Spring boot REST API application running in any EC2 with exposing multiple APIs and I am not using AWS API Gateway for them.
I need multiple users for accessing multiple APIs with different roles (Admin, WriteUser, ReadUser).
I can create Users and Groups in Cognito Userpool and can have multiple Roles in IAM.
How can I configure AWS Cognito's UserPool and IdentityPool to authorize different APIs access based on User role ? Is there any way to define such "API allow rules based on role" in Policy (associated with IdentityPool/Resource server) without using API Gateway.
Thanks in Advance.
I did implementation with Spring security auth2 with Custom JWTFilter, which will get token from request and validate agains cognito pool's JWKs validation file.
User enters username/password in UI.
UI calls back-end Application, which calls Cognito.
Cognito returns JWT Tokens (Idtoken, accessToken, refreshTokens)
Backend application sends IdToken to UI as token and also store in cache(could be any DB also).
UI sends this IdToken for next calls.
JWTFilter does below steps, for authentication :
Backend Application takes token, validate in cache if present then validate signature and expiry with Cognito JWKs and get details out of it.
Backend Application creates UserAuthentication extending AbstractAuthenticationToken and populate details from parsed token values and store in context (session)
Authorization for resource would be done as per config mentioned in websecurity configurations.
You need to implement JWT token approach with Spring security(can use spring security auth 2 impl.)
So auth steps will followings:
User will enter his/her credential on your front-end app.
Your auth service will send those credentials to AWS Cognito to verify.
If User credentials will verify then with the help of AWS Cognito user response, you will create a payload and then create a JWT token.
Sign JWT token with the public-private key so you can share your public key with your other services to validate JWT token.
Then JWT token return to front-end app.
Now Spring Security will secure you all private routes.
You also need to create a filter so when front-end app creates ant request to the server with attaching JWT in the header, then your filter will validate JWT token (as an authentication header) if the token is valid then will create security context for current request else throw 401 status.
We can create Spring Boot resource server, keeping Cognito as Identity Provider.
Dependency:
<!-- Spring Security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
Spring Security Configuration:
EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class OAuth2ResourceServerSecurityConfiguration extends ResourceServerConfigurerAdapter {
private final ResourceServerProperties resource;
public OAuth2ResourceServerSecurityConfiguration(ResourceServerProperties resource) {
this.resource = resource;
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.cors();
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.antMatchers("/actuator/health").permitAll()
.anyRequest().authenticated();
}
// Note: Cognito Converter
#Bean
public TokenStore jwkTokenStore() {
return new JwkTokenStore(
Collections.singletonList(resource.getJwk().getKeySetUri()),
new CognitoAccessTokenConverter(),
null);
}
}
Cognito Access Token Converter:
Here we are converting the Cognito claims to Spring Security consumable format.
For Authorization, we will make use of Cognito Groups. We create two groups, ROLE_ADMIN & ROLE_EMPLOYEE. We map users to each group. When the user is authenticated, we get the Cognito group as claims. We make use of this to set Spring Security Authorities for the user.
#Component
public class CognitoAccessTokenConverter extends JwtAccessTokenConverter {
// Note: This the core part.
private static final String COGNITO_GROUPS = "cognito:groups";
private static final String SPRING_AUTHORITIES = "authorities";
private static final String COGNITO_USERNAME = "username";
private static final String SPRING_USER_NAME = "user_name";
#SuppressWarnings("unchecked")
#Override
public OAuth2Authentication extractAuthentication(Map<String, ?> claims) {
if (claims.containsKey(COGNITO_GROUPS))
((Map<String, Object>) claims).put(SPRING_AUTHORITIES, claims.get(COGNITO_GROUPS));
if (claims.containsKey(COGNITO_USERNAME))
((Map<String, Object>) claims).put(SPRING_USER_NAME, claims.get(COGNITO_USERNAME));
return super.extractAuthentication(claims);
}
}
application.properties
server:
port: 8081
security:
oauth2:
resource:
userInfoUri: https://<cognito>.auth.eu-west-1.amazoncognito.com/oauth2/userInfo
tokenInfoUri: https://<cognito>.auth.eu-west-1.amazoncognito.com/oauth2/token
jwk:
key-set-uri: https://cognito-idp.<region>.amazonaws.com/<user-pool-id>/.well-known/jwks.json
client:
clientId: <client-id>
For complete article, refer: Integrate Spring Boot Resource Server with Cognito Identity Provider

Keycloak Custom User Federation and Identity Provider Working Order

I have two custom authentication method on Keycloak.
One of them is custom implemented user federation. I configured it for X realm. System uses this implementation for login with username / password method. This implementation calls my federation service and it validates sent user. It works successfully and authenticates federated users.
Second one is an identity broking (openid connect). I configured a custom openid provider to Y realm. It works successfully and validates provider's users.
I configured both of them to same realm. When i try to use login with custom identity provider, authentication flow works correctly. In the end of flow, configured user federation (custom implemented user federation) triggers with username which comes from identity broking (custom identity provider) login process and it calls my federation service again.
When i try to login with identity providers, i do not want the user federation (custom implemented user federation) to work. It must work only when i try to login with username / password login.
How can i block working of user federation on this scenario?
Please share your experience.
Thanks
I had the same issue of the identity provider and custom user federation not both working at the same time. My solution wasn't to block user federation but to change my custom user federation code. The implementation of the method that returns the user from the UserLookupProvider interface (getUserByEmail/Username/Id) must be coded to return a AbstractUserAdapterFederatedStorage. This implementation of UserModel provides implementation for methods needed by keycloak to internalize users from identity providers. Additionally I implemented UserQueryProvider interface along with UserStorageProvider, UserLookupProvider, CredentialInputValidator to be able to see these users from the keycloak admin console.
From their documentation:
Keycloak comes with a helper class org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage that will delegate every single UserModel method except get/set of username to user federated storage.
...interface UserQueryProvider. If you do not implement this interface, then users will not be viewable in the admin console. You’ll still be able to login though.
Ref: https://www.keycloak.org/docs/latest/server_development/#_user-storage-spi
Code:
note: A custom keycloak user storage should have at least two classes. One main one that does the heavy lifting that must implement at least UserStorageProvider. And another that is a factory that calls this class. More on this on their Server Development guide. The following code goes in the main class (not the factory):
#Override
public UserModel getUserByUsername(String username, RealmModel realm) {
[...]
userModel = createUserModel(username, realm);
[...]
return userModel;
}
protected UserModel createUserModel( String username, RealmModel realm) {
return new AbstractUserAdapterFederatedStorage(session, realm, model) {
#Override
public String getUsername() {
return username;
}
#Override
public void setUsername(String username) {
//retrieves user through repository and sets the keycloak user to its username. (Seems redundant but works!)
usersService.getUserDetails(username).setUsername(username);
}
};
}

Wildfly custom JAAS LoginModule with custom Principal in RunAs and unauthenticated

I'm looking for help with custom Principal used in custom LoginModule on Wildfly10/Jboss 7.
I would like to put some additional data into my custom principal connected with authorization as well as audit data such as sessionId gathered from HttpRequest in my custom LoginModule.
In EJB implementation i'm getting my custom principal object from sessionContext.getCallerPrincipal() and it works pretty well when user is authenticated by my LoginModule (where i produce desired principal object).
Problem starts when i turn off BASIC authentication on any webservice or when i'm using #RunAs implementation in my EJB call stack.
In case with authentication turned off im getting "anonymous" SimlePrincipal object from getCallerPrincipal() and SimplePrincipal with one role in case with #RunAs.
Is there any way to force Jboss to use my custom LoginModule or any other module
where i can produce my custom principal and put in into sessionContextCaller principal?

WAS Liberty - How to trigger JAAS module?

Using WAS 8558 and on one of the URL pattern, need to invoke JAAS module.
Entry in web.xml
<security-constraint>
<display-name>SampleConstraint</display-name>
<web-resource-collection>
<web-resource-name>Sample</web-resource-name>
<url-pattern>/wasauth</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<description>
Users allowed access to spoke Identity Provider</description>
<role-name>FIMAnyAuthenticated</role-name>
<role-name>FIMUserSelfCareAnyAuthenticated</role-name>
</auth-constraint>
Entry in server.xml
<jaasLoginContextEntry id="system.FIM_OUTBOUND" name="system.FIM_OUTBOUND" loginModuleRef="myCustom, hashtable, userNameAndPassword, certificate, token"/>
<jaasLoginModule id="myCustom" className="com.*.SampleLoginModule" controlFlag="REQUIRED" libraryRef="customLoginLib">
</jaasLoginModule>
<library id="customLoginLib" apiTypeVisibility="spec, ibm-api, api">
<fileset dir="/" includes="com.**_8.0.0.jar"/>
This flow is using Federated repository feature (Liberty) for authentication.
Above mentioned settings allow user to get authenticated against repository however JAAS module is not getting invoked.
If I convert JAAS entry to system.WEB_INBOUND using WSLoginModuleProxy - JAAS module gets invoked.
Liberty does have appSecurity-2.0 feature enabled.
Is there any other configuration which needs to be done?
When the Liberty profile performs authentication for protected web resources it uses the system.WEB_INBOUND JAAS login configuration entry. So any custom login modules that you have configured in it will be called.
If you have configured your custom login modules in your own or a different JAAS configuration it will not be called by the server during web authentication. Your application. however, can call it directly.

Custom Glassfish realm

I want to implement custom authorization in Glassfish 4. So I must implemented Realm class and LoginModule class extends AppservPasswordLoginModule. But I need to store in the principal class not only name of the principal but also id from database. How can I use my own implementation of Principal class?