Spring Security - Specify which X509 can access which specific endpoint - java

Small question regarding how to use Spring Security to specify which client certificate can access what specific predefined endpoint, please.
By predefined endpoint, I mean the web application has default endpoint (not those I defined via #RestController) such as the actuator endpoint /actuator/health, /actuator/prometheus, or the Spring Cloud Config endpoints, such as /config/myservice/ There is no possibility to #PreAuthorize.
I would like to just specify which client certificate can access which endpoint, such as:
Client certificate with UID=Alice can access /actuator/health and /config/myservice.
Client certificate with UID=Bob can access /actuator/prometheus
There are many examples online on, How to extract X509 certificate:
https://www.baeldung.com/x-509-authentication-in-spring-security
https://blog.codecentric.de/en/2018/08/x-509-client-certificates-with-spring-security/
But how to configure it in the application, i.e., this sort of mapping of which certificate can access what?
Thank you

#Setu gave you the essential information to solve the problem.
Please, consider the following code adapted from the one that can be found in one of the articles you cited:
#SpringBootApplication
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class X509AuthenticationServer extends WebSecurityConfigurerAdapter {
...
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
// Define the mapping between the different endpoints
// and the corresponding user roles
.antMatchers("/actuator/health").hasRole("ACTUATOR_HEALTH")
.antMatchers("/actuator/prometheus").hasRole("ACTUATOR_PROMETEUS")
.antMatchers("/config/myservice").hasRole("CONFIG_MYSERVICE")
// Please, adjust the fallback as appropriate
.anyRequest().authenticated()
.and()
// Configure X509Configurer (https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/config/annotation/web/configurers/X509Configurer.html)
.x509()
.subjectPrincipalRegex("CN=(.*?)(?:,|$)")
.userDetailsService(userDetailsService())
;
}
#Bean
public UserDetailsService userDetailsService() {
// Ideally this information will be obtained from a database or some
// configuration information
return new UserDetailsService() {
#Override
public UserDetails loadUserByUsername(String username) {
Objects.requireNonNull(username);
List<GrantedAuthority> authorities = null;
switch (username) {
// Match the different X509 certificate CNs. Maybe you can use the
// X509 certificate subject distinguished name to include the role in
// some way and obtain it directly with the subjectPrincipalRegex
case "Alice":
authorities = AuthorityUtils
.commaSeparatedStringToAuthorityList("ROLE_ACTUATOR_HEALTH, ROLE_CONFIG_MYSERVICE");
break;
case "Bob":
authorities = AuthorityUtils
.commaSeparatedStringToAuthorityList("ROLE_ACTUATOR_PROMETHEUS");
break;
default:
throw new UsernameNotFoundException(String.format("User '%s' not found!", username));
}
return new User(username, "", authorities);
}
};
}
}
Please, adapt the code as you need to meet your actual endpoints and users.

Related

How to implement a custom OAauth2 authorization with Spring?

Looking at the new spring-security-oauth2-authorization-server library, I need to implement a custom authorization server for a resource server. Actually, I intend to manually implement the PRIVATE_KEY_JWT Authentication method that is either not yet implemented or lacking examples. I followed one good example that uses this approach (the authorization server issuing the access token to the resource server). The resource server has a simple configuration that points to my issuer (authorization server) as in the code:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://auth-server:9000
but this approach uses client-id and client-secret credentials like:
#Bean
#Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
return http.formLogin(Customizer.withDefaults()).build();
}
#Bean
public RegisteredClientRepository registeredClientRepository() {
RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("client_id123")
.clientSecret("{noop}secret123")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.scope("https://purl.imsglobal.org/spec/lti-ags/scope/score")
.build();
return new InMemoryRegisteredClientRepository(registeredClient);
}
I appreciate if anyone could refer me an example of either a PRIVATE_KEY_JWT implementation or a custom authorization implementation that receives a JWT and issues an access token.
In application.yml set up the location of the certs, and create a config class RsaKeyProperties to load them:
rsa:
privateKey: classpath:certs/private.pem
publicKey: classpath:certs/public.pem
Then in WebSecurityConfig:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
#Bean
protected SecurityFilterChain configure(
BasicAuthFilter basicAuthFilter,
ActivitiAuthenticationFilter activitiAuthenticationFilter,
HttpSecurity http) throws Exception {
return http
// Disabling CSRF is safe for token-based API's
.csrf().disable()
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeRequests(auth -> {
auth.antMatchers(
"/api/authenticate/**",
"/api/tenants/**").permitAll();
auth.antMatchers("/api/**").authenticated();
// When an exception is thrown, ErrorMvcAutoConfiguration sets stuff up so that /error is called
// internally using an anonymous user. Without this line, the call to /error fails with a 403 error
// because anonymous users would not be able to view the page.
auth.antMatchers("/error").anonymous();
})
.build();
}
#Bean
public JwtDecoder jwtDecoder(RsaKeyProperties rsaKeyProperties) {
return NimbusJwtDecoder.withPublicKey(rsaKeyProperties.publicKey()).build();
}
#Bean
public JwtEncoder jwtEncoder(RsaKeyProperties rsaKeyProperties) {
JWK jwk = new RSAKey.Builder(rsaKeyProperties.publicKey())
.privateKey(rsaKeyProperties.privateKey())
.build();
JWKSource<SecurityContext> jwks = new ImmutableJWKSet<>(new JWKSet(jwk));
return new NimbusJwtEncoder(jwks);
}
}
The "SCOPE_" prefix that the identity server library was adding in the JWT didn't work well with spring security as far as I could figure out, so I removed that:
/**
* For some reason the JwtGrantedAuthoritiesConverter defaults to adding the prefix "SCOPE_" to all
* the claims in the token, so we need to provide a JwtGrantedAuthoritiesConverter that doesn't do
* that and just passes them through.
*/
#Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
grantedAuthoritiesConverter.setAuthorityPrefix("");
JwtAuthenticationConverter authConverter = new JwtAuthenticationConverter();
authConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
return authConverter;
}

Adding a resource server to an existing JSF/Spring security application

Currently, we've implemented a centralized authorization server using the oauth2.0 protocols and a password flow. I will post the user, pwd, secret and clientID to the auth server endpoint, get a JWT back and be on my way.
What I currently have in this existing "client" application is some spring security boilerplate that configures traffic based on roles.
What I need to do is (I think) - When I log into the application, I will receive a token. That token is stored in the session and whenever I access a page on that application, I will either confirm the token is still good or refresh the token so that I can continue accessing secured content.
I am assuming I need to do the following:
Add a resource server to my existing application (#EnableResourceServer), so that traffic flows through the oauth2.0 process.
Configure said resource server (similar to my WebSecurityConfigurerAdapter configure class), so that the token is checked
Keep everything else (roles, file structure, etc) the same
How do I do this with Spring? I have looked at tutorials and most everything is either same home resource and auth server solutions or has minimal explanation for how something can be configured.
Property file:
security.oauth2.client.client-id= clientid
security.oauth2.client.client-secret= clientsecret
security.oauth2.client.access-token-uri= http://localhost:1234/oauth/token
security.oauth2.client.user-authorization-uri= http://localhost:1234/oauth/authorize
security.oauth2.resource.service-id=res_id
security.oauth2.resource.user-info-uri= http://localhost:1234/me
security.oauth2.resource.token-info-uri= http://localhost:1234/oauth/check_token
security.oauth2.resource.token-type= Bearer
Current WebSecurityConfigurerAdapter:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#ComponentScan("com.testproj")
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationProvider authProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
// Authentication control
http
.authorizeRequests()
.antMatchers("/login.xhtml**").permitAll() // All everyone to see login page
.antMatchers("/login").permitAll() // All everyone to see login page
.antMatchers("/views/**").hasAnyAuthority("USER", "ADMIN")
.antMatchers("/javax.faces.resource/**").permitAll() // All everyone to see resources
.antMatchers("/resources/**").permitAll() // All everyone to see resources
.anyRequest().authenticated(); // Ensure any request to application is authenticated
// Login control
http
.formLogin()
.loginPage("/login.xhtml")
.loginProcessingUrl("/login")
.successForwardUrl("/views/home.xhtml")
.failureUrl("/login.xhtml?error=true");
// not needed as JSF 2.2 is implicitly protected against CSRF
http
.csrf().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
}
Custom authenticator:
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private AppUserDAO appUserDAO;
private String accessTokenUri = "http://localhost:1234/oauth/token";
private String clientId = "clientid";
private String clientSecret = "clientsecret";
public AccessTokenProvider userAccessTokenProvider() {
ResourceOwnerPasswordAccessTokenProvider accessTokenProvider = new ResourceOwnerPasswordAccessTokenProvider();
return accessTokenProvider;
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
final String username = authentication.getName();
final String password = authentication.getCredentials().toString();
List<String> scopes = new ArrayList<String>();
scopes.add("read");
final ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
resource.setUsername(username);
resource.setPassword(password);
resource.setAccessTokenUri(accessTokenUri);
resource.setClientId(clientId);
resource.setClientSecret(clientSecret);
resource.setGrantType("password");
resource.setScope(scopes);
// Generate an access token
final OAuth2RestTemplate template = new OAuth2RestTemplate(resource, new DefaultOAuth2ClientContext(new DefaultAccessTokenRequest()));
template.setAccessTokenProvider(userAccessTokenProvider());
OAuth2AccessToken accessToken = null;
try {
accessToken = template.getAccessToken();
System.out.println("Grabbed access token from " + accessTokenUri);
}
catch (OAuth2AccessDeniedException e) {
if (e.getCause() instanceof ResourceAccessException) {
final String errorMessage = String.format(
"While authenticating user '%s': " + "Unable to access accessTokenUri '%s'.", username,
accessTokenUri);
throw new AuthenticationServiceException(errorMessage, e);
}
throw new BadCredentialsException(String.format("Access denied for user '%s'.", username), e);
}
catch (OAuth2Exception e) {
throw new AuthenticationServiceException(
String.format("Unable to perform OAuth authentication for user '%s'.", username), e);
}
// Determine roles for user
List<GrantedAuthority> grantList = ...
// Create custom user for the principal
User user = .....
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user, null /*dont store password*/, grantList);
return token;
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
Resource server:
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
????
}
Edit:
So I moved my httpsecurity configuration into my resource server configuration and I get "Access Denied" when I attempt to get to the /views/home.xhtml page.
The log indicates the principal is anonymous (it wasn't like that in securityconfig)
Secure object: FilterInvocation: URL: /views/home.xhtml; Attributes: [#oauth2.throwOnError(hasAuthority('USER'))]
Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken#fc4a062: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true;
Edit 2
This might be a question about what is possible with oauth2, because I'm kind of going insane.
There are 4 pieces according to the oauth specs - Auth server, resource server, client and resource owner
I guess my question is - can I actually combine the resource server and client? The resource owner would sign into my app (client), get authorization from the auth server to see their resources on the resource server/client.
I really wish I could find a good example of this - it seems so definitive and common.

Authentication using cookies in spring boot

//Part of my Controller class
#RequestMapping("/login")
public String login(HttpServletRequest request,HttpServletResponse response) {
request.setAttribute("mode", "MODE_LOGIN");
return "welcomepage";
}
#RequestMapping ("/login-user")
public String loginUser(#ModelAttribute User user, HttpServletRequest request,HttpServletResponse response) {
if((userService.findByUsernameAndPassword(user.getUsername(), user.getPassword())!=null)) {
Cookie loginCookie=new Cookie("mouni","user.getUsername()");
loginCookie.setMaxAge(30*5);
response.addCookie(loginCookie);
return "homepage";
}
else {
request.setAttribute("error", "Invalid Username or Password");
request.setAttribute("mode", "MODE_LOGIN");
return "welcomepage";
}
}
I am doing a library management project on java spring boot.
I have one problem, i would like to do authentication using cookies.
In brief, Once after user logged in with his credentials, username should be saved as cookie value. Next time when user is going to login, he can just enter username and should be logged in successfully.
Could someone please help me out
Since security is a complex matter, I recommend using Spring Security, even though you're tasked to do it without. To illustrate the complexity about security, I can already tell you that your current code has a vulnerability, since you're trusting a plaintext username cookie as your sole authentication. Spring Security on the other hand uses a key to generate a remember me cookie so that it is much more difficult to impersonate someone (unless you know the key).
So, if you would be using Spring Security, the first thing you need to do is to create a UserDetailsService, which has a method called loadByUsername(). To implement this, you could use your UserService and use the User builder to construct a Spring Security user object:
public class MyUserDetailsService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if ("admin".equalsIgnoreCase(username)) {
return User.builder()
.username(username)
// This should contain the hashed password for the requested user
.password("$2a$10$T5viXrOTIkraRe2mZPyZH.MAqKaR6x38L.rbmRp53yQ8R/cFrJkda")
// If you don't need roles, just provide a default one, eg. "USER"
.roles("USER", "ADMIN")
.build();
} else {
// Throw this exception if the user was not found
throw new UsernameNotFoundException("User not found");
}
}
Be aware, in contrary to your original UserService.findByUsernameAndPassword() you do not have to check the password by yourself, just retrieve the user object and pass the hashed password.
The next step is to provide a proper PasswordEncoder bean. In my example I'm using BCrypt with 10 rotations, so I created the following bean:
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(10);
}
#Bean
public UserDetailsService userDetailsService() {
return new MyUserDetailsService();
}
}
The next step is to configure Spring Security. For example:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/**")
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.html").permitAll()
.loginProcessingUrl("/login-user").permitAll().usernameParameter("username").passwordParameter("password")
.defaultSuccessUrl("/welcome.html")
.and()
.rememberMe()
.alwaysRemember(true)
.tokenValiditySeconds(30*5)
.rememberMeCookieName("mouni")
.key("somesecret")
.and()
.csrf().disable();
}
In this case, all endpoints (/**) will be secured, you'll have a login form at login.html containing two form fields (username and password). The destination of the form should be /login-user and when a user is successfully logged in, he will be redirected to /welcome.html.
Similar to what you wrote in your code, this will generate a cookie called mouni containing a value (no longer a plain username) and it will be valid for 150 seconds, just like in your example.
I'm disabling CSRF here because I'm using a simple HTML form, and otherwise I would have to add a templating engine to pass the CSRF key. Ideally, you should keep this enabled.
You are using Spring framework which has the capability for the same which you are trying to achieve. so why to do it manually?
Have a look at spring security.
https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/

Can we use httpbasic and OAuth2 both for an API depending on path variable?

In my application I want to provide OAuth2 security only to some specific API calls. My question is can I provide HttpBasic or Oauth2 authentication based on the path variable?
Below are two scenarios I will be considering.
1) Lets say for user(whose name is provided in path variable) xyz, if xyz does not have the feature of OAuth, I want to authenticate it using httpBasic
2) If another user abc has feature of OAuth, I want to authenticate it using Oauth/OpenId connect.
I have a table which assigns features to user, below is the glimpse of the table.
Name , Feature
xyz, HttpBasic
abc, Oauth
Okay I did some research on my own and able to find a solution. Here's what I did,
-Created one httpbasic configuration with WebSecurityConfigurerAdapter, now before any interceptor begins it task I have created one request matcher which will check if the authorization header is Basic or Bearer.
//By default this filter order is 100 and OAuth has filter order 3
#Order(2)
public class MicroserviceSecurityConfigurationHttpBasic extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().exceptionHandling()
.authenticationEntryPoint(customAccessDeniedHandler())
.and().headers().frameOptions().disable()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.requestMatcher(new BasicRequestMatcher())
.authorizeRequests()
.antMatchers("/api/**").authenticated()
.and().httpBasic();
}
private class BasicRequestMatcher implements RequestMatcher {
#Override
public boolean matches(HttpServletRequest httpRequest) {
String auth = httpRequest.getHeader("Authorization");
String requestUri = httpRequest.getRequestURI();
//Fetching Identifier to provide OAuth Security to only specific urls
String identifier= requestUri.substring(requestUri.lastIndexOf("/") + 1, requestUri.length());
//Lets say for identifier ABC only, I want to secure it using OAuth2.0
if (auth != null && auth.startsWith("Basic") && identifier.equalsIgnoreCase("ABC")) {
auth=null;
}
//For ABC identifier this method will return null so then the authentication will be redirected to OAuth2.0 config.
return (auth != null && auth.startsWith("Basic"));
}
}
}
-After this I have created OAuth2.0 configuration with ResourceServerConfigurerAdapter, here is the glimpse of it.
//Default filter order=3 so this will be executed after WebSecurityConfigurerAdapter
public class MicroserviceSecurityConfiguration extends ResourceServerConfigurerAdapter {
...
//Here I am intercepting the same url but the config will look for bearer token only
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().exceptionHandling()
.and().headers().frameOptions().disable()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().authorizeRequests()
.antMatchers("/api/**").authenticated();
}
}
References : https://github.com/spring-projects/spring-security-oauth/issues/1024
Spring security with Oauth2 or Http-Basic authentication for the same resource

Implementing authorization code flow grant type OAuth2

I am in a real problem here and would require your help. I work for a bank and have been assigned a task of implementing an OAuth2 service using Spring Boot, I have been exploring since last one week and have been able to implement a password flow grant type OAuth2 service, but now I have a few questions as my seniors said that password flow is not the right fit for our use case. First I would like to explain the use case:
Step 1: user will hit the application URL of the web app where no login is needed and before the application loads the OAuth2 service will be hit with logged in AD (system) user id.
Step 2. The OAuth2 service should authenticate the user using ldap with the given user id and return back all groups the user is part of along with the access token which will be used to access the API's there after
Now I have the below queries:
Which grant type is best suited for my need, from what I have read authorization code grant type seems to be the right fit? Or is it implicit?
Depending on the answer for question 1 what code changes do I need to make in the below code:
Code snippet for my authorization server:
Oauth2AuthserverApplication.java
#SpringBootApplication
#EnableAuthorizationServer
public class Oauth2AuthserverApplication {
public static void main(String[] args) {
SpringApplication.run(Oauth2AuthserverApplication.class, args);
}
}
OAuth2Congig.java
#Configuration
public class Oauth2Config extends AuthorizationServerConfigurerAdapter {
private String clientId = "client";
private String clientSecret = "secret";
private String privateKey = "private-key";
private String publicKey = "public-key";
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Bean
public JwtAccessTokenConverter tokenEnhancer() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(privateKey);
converter.setVerifierKey(publicKey);
return converter;
}
#Bean
public JwtTokenStore tokenStore() {
return new JwtTokenStore(tokenEnhancer());
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.authenticationManager(authenticationManager).
tokenStore(tokenStore())
.accessTokenConverter(tokenEnhancer());
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security)
throws Exception {
security.tokenKeyAccess("permitAll()").
checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws
Exception {
clients.inMemory().withClient(clientId).
secret(clientSecret).scopes("read", "write")
.authorizedGrantTypes("password",
"refresh_token").accessTokenValiditySeconds(20000)
.refreshTokenValiditySeconds(20000);
}
}
SecurityConfiguration.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
CustomDetailsService customDetailsService;
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
#Override
#Autowired
protected void configure(AuthenticationManagerBuilder auth) throws
Exception {
auth.userDetailsService(customDetailsService).
passwordEncoder(encoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().
and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception
{
return super.authenticationManagerBean();
}
}
Not pasting the model, dao, and service code of the authorization server class as they are not relevant for this question.
Code Snippet from resource server project:
OAuth2ResourceserverApplication.java
#SpringBootApplication
#EnableResourceServer
#RestController
public class Oauth2ResourceserverApplication {
public static void main(String[] args) {
SpringApplication.run(Oauth2ResourceserverApplication.class, args);
}
#RequestMapping(value="/api")
public String success() {
return "SUCCESS";
}
}
JwtConverter.java
#Component
public class JwtConverter extends DefaultAccessTokenConverter implements
JwtAccessTokenConverterConfigurer {
#Override
public void configure(JwtAccessTokenConverter converter) {
converter.setAccessTokenConverter(this);
}
}
SecurityConfiguration.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().
and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER);
}
}
application.yml
server:
port: 8081
security:
oauth2:
resource:
filter-order: 3
jwt:
key-value: private-key
OAuth2 has 4 grant types. To quickly understand the difference between 'Resource Owner Password Credential', 'Authorization Code', 'Implicit', let us compare them side-by-side:
NOTE: The full explanation is available at: https://blog.oauth.io/oauth2-flow-grant-types-in-pictures/.
To answer your question:
Which grant type is best suited for my need, from what I have read authorization code grant type seems to be the right fit? Or is it implicit?
If you compare based on 'Security' the purple bar, 'Authorization Code' is best. However you can see that it has the concept of Guard (backend) that performs access to the User data store on behalf of the App (frontend), i,.e., the App never has access to the Key/Access Token directly, as the Key is retrieved through the exchange of username/password between the User and the OAuth Server, and then passed on to the Guard.
The 'Resource Owner Password Credential' that you implemented is the least secure as the username/password is handed over to the App for the App to do everything that the User can without the User's further consent. However, in your scenario, the App and the User data store both belong to you so that alleviates the security issue.
Depending on the answer for question 1 what code changes do I need to make in the below code:
The full flow of Resource Owner Password Credential grant type you implemented is the left part of the image below and the Authorization Code grant type is the right part. As you can see, there are generally 5 steps. For Resource Owner Password Credential, some steps are not necessary, i.e., marked 'N.A.'.
NOTE:
The 'cloud' represents the App
The 'www' represents the User/Browser
The 'safe' represents the OAuth Server
To get from the left-side to the right-side, changes you required will be:
Step 1. If your OAuth Server is going to support different Apps, then it needs to support App pre-registration to get client id/secret. If you only have one App then you can skip this.
Step 2. Instead of the App prompting for username/password now the App will redirect the User to the OAuth Server to perform username/password authentication
Step 3. After authenticating the User the OAuth Server can prompt the User about what kind of permissions (e.g., read email, update profile, etc.) she wants to grant the App
Step 4. The OAuth Server instead of handing the Key/Access token to the App, it hands a code to User which then passed it to the App
Step 5. The App then exchanges the code for the Key/Access Token with the OAuth Server.
Once you get the Key/Access Token you can call any protected API on a different server which can then validate the Key/Access Token with the OAuth Server before responding to the API request, e.g., return the groups a User belongs to.

Categories

Resources