Why Resource Server has to know client_id in Spring OAuth2? - spring-security

I'm implementing OAuth2 authorization using Spring Boot. I have already Authorization Server and Resource Server, now I want to access resources from Resource Server using client_credentials grant type.
I'm little confused about it, because in Resource Server I have to add client_id and client_secret. But why Resource Server really need it?
As I understand this concept client should get from Authorization Server using client credentials his access token. And then send this access token to Resource Server without any client credentials.
So why Resource Server also need some client credentials? Resource Server and client are two separeted entities, I don't understand why Resource Server has to know about client_id and client_secret.
Why access token is not enough to authenticate? check_token endpoint can return list of resources that can be accessed with this token and if client has this token, this means that he is already authenticated with client credentials to get this token.
What if I want to access from multiple different clients to this Resource Server?
Resource Server config:
#Configuration
#RestController
#EnableWebSecurity
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/**").authenticated()
.and()
.httpBasic().disable();
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources
.resourceId("translate-service");
}
}
Resource server properties:
security.oauth2.resource.user-info-uri=http://localhost:8090/user
security.oauth2.resource.token-info-uri=http://localhost:8090/oauth/check_token
security.oauth2.client.client-id=XXXX
security.oauth2.client.client-secret=XXXX
If I wont set client properties Spring will log warning:
Null Client ID or Client Secret detected. Endpoint that requires authentication will reject request with 401 error.
And authentication will not work.
Maybe I doing something wrong and there is some solution to not provide client_id in Resource Server?

If you use RemoteTokenServices your Resource Server is also an additional client of the Authorization Server, see OAuth 2 Developers Guide:
An alternative is the RemoteTokenServices which is a Spring OAuth features (not part of the spec) allowing Resource Servers to decode tokens through an HTTP resource on the Authorization Server (/oauth/check_token). RemoteTokenServices are convenient if there is not a huge volume of traffic in the Resource Servers (every request has to be verified with the Authorization Server), or if you can afford to cache the results. To use the /oauth/check_token endpoint you need to expose it by changing its access rule (default is "denyAll()") in the AuthorizationServerSecurityConfigurer, e.g.
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')").checkTokenAccess(
"hasAuthority('ROLE_TRUSTED_CLIENT')");
}
In this example we are configuring both the /oauth/check_token endpoint and the /oauth/token_key endpoint (so trusted resources can obtain the public key for JWT verification). These two endpoints are protected by HTTP Basic authentication using client credentials.
and OAuth2 Boot:
2.4 How to Configure the Token Info Endpoint
The token info endpoint, also sometimes called the introspection endpoint, likely requires some kind of client authentication, either Basic or Bearer. Generally speaking, the bearer token in the SecurityContext won’t suffice since that is tied to the user. Instead, you’ll need to specify credentials that represent this client, like so:
spring:
security:
oauth2:
client:
clientId: client-id
clientSecret: client-secret
resource:
tokenInfoUri: https://issuer/oauth2/check_token
By default, this will use Basic authentication, using the configured credentials, to authenticate against the token info endpoint.

Related

Logout at Spring Authorization Server doesn’t terminate user session in Keycloak

I am trying to implement an end-user initiated logout mechanism.
I have 4 main entities involved.
Client: an Angular app registered as a public OIDC client on Keycloak
Keycloak: behaves as an identity broker
Spring Authorization Server: identity provider registered on Keycloak
Resource Server: a Spring Boot application with a secure REST endpoint
When the end-user initiates a logout, a call is made by the Angular application to Keycloak’s end_session_endpoint. I have configured the logout URL for my identity provider (Spring Authorization Server) in Keycloak as http://localhost:9000/logout which is the default Spring Security logout endpoint.
FYI: I haven't enabled BackChannel or FrontChannel logout.
In the Network tab of Developer Console, the sequence of calls happen as below:
http://127.0.0.1:8080/auth/realms/SpringBootKeycloak/protocol/openid-connect/logout?client_id=hello-world&post_logout_redirect_uri=http%3A%2F%2Flocalhost%3A4200%2F&id_token_hint=...
http://localhost:9000/logout?state=ff57bb5b-4a79-4e68-bd39-fe7a33ec8788&id_token_hint=...&post_logout_redirect_uri=http%3A%2F%2F127.0.0.1%3A8080%2Fauth%2Frealms%2FSpringBootKeycloak%2Fbroker%2Foidc%2Fendpoint%2Flogout_response
While inspecting the DEBUG logs in the Spring Authorization Server, I am able to see the logout happen for that particular end-user including invalidating the JSESSIONID, however the session doesn’t terminate in Keycloak which causes the user to stay logged in and access the secure REST endpoint.
Is the Spring Authorization Server expected to return a specific response back to Keycloak to convey that the logout process is complete at it’s end and that Keycloak can end the session now?
This is my logic for logout at Spring Authorization Server.
#Bean
#Order(Ordered.LOWEST_PRECEDENCE)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(
authorize -> authorize.mvcMatchers("/hello/**").permitAll().anyRequest().authenticated())
.formLogin(form -> form.loginPage("/login").permitAll())
.addFilterAfter(cookieFilter, ChannelProcessingFilter.class)
.logout().logoutSuccessUrl("http://localhost:4200");
return http.build();
}
I am totally clueless about what I am missing to make Keycloak end the session at it’s end.
Any leads will be greatly appreciated.
Thank you!
Update: A post_logout_redirect_uri parameter is sent in the logout request to the Spring Authorization Server. I am not able to see a redirection happen to this URI which may be the reason that Keycloak session doesn't terminate.
post_logout_redirect_uri=http://127.0.0.1:8080/auth/realms/SpringBootKeycloak/broker/oidc/endpoint/logout_response
Got this to work finally!
The logoutSuccessUrl("http://localhost:4200") is incorrect.
As I mentioned in the Update part of my question that Keycloak sends a post_logout_redirect_uri parameter (inspected the logout endpoint in the Network calls in Chrome Developer Console), the logoutSuccessUrl or the redirectUri should be set to
http://127.0.0.1:8080/auth/realms/SpringBootKeycloak/broker/oidc/endpoint/logout_response
Also, note that the above URL takes in state as a request parameter. Initially, it gave me a 400 Bad Request as I didn't send the state.
Since I was required to intercept the state parameter when logout is initiated at the Spring Authorization Server, I added a custom logout handler to do the job.
#Component
public class IdpLogoutHandler implements LogoutHandler {
private static final String STATE = "state";
#Override
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
String state = request.getParameter(STATE);
try {
response.sendRedirect(
"http://127.0.0.1:8080/auth/realms/SpringBootKeycloak/broker/oidc/endpoint/logout_response?state="
+ state);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Updated WebSecurityConfig:
http.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(
authorize -> authorize.mvcMatchers("/hello/**").permitAll().anyRequest().authenticated())
.formLogin(form -> form.loginPage("/login").permitAll())
.addFilterAfter(cookieFilter, ChannelProcessingFilter.class).logout()
.addLogoutHandler(customLogoutHandler);

Spring Security SAML2 and Username Password Login in the same API

I am working on implementing a login feature to secure a REST API in Spring Boot and I am struggling to get both the Username/Password JWT authentication and SAML2 authentication to work at the same time. The configuration I give highest priority to is the one that works no matter what. The goal is to use the SAML Provider as an alternative Identity provider for the service that has an internal authentication flow, then grant a user a JWT regardless if they use SAML or the app's login.
I'm following this example for the SAML, and have built out a username/password JWT grant that is working as well. But they only work independently or when given highest priority.
When I do something with multiple configs like:
#EnableWebSecurity
class MultiConfig {
#Config
#Order(1)
static class JWTConfig extends WebSecurityConfigurerAdapter {
// working JWT configs
}
#Config
#Order(2)
static class SAMLConfig extends WebSecurityConfigurerAdapter {
// working SAML Configs
}
}
The configs work separately, but when combined, Spring Security is only working on whatever has highest priority.
I am working with the new(ish) SAML2 libraries in Spring Security Core as of 5.6.3.
The specific error I'm getting is on a sample endpoint:
#RequestMapping(value = "/valid/saml/landing", method = POST).
String testSaml(Model model, #AuthenticationPrincipal Saml2AuthenticatedPrincipal principal) {
// Supposed to get user info here
}
When combined and priority is given to Username/Password JWT, the above endpoint says that "principal is null". The ultimate goal is to treat a SAML assertion as an alternative Identity Authentication and grant the user an application JWT.
It turns out, the configs were correct all along and it's a SAML Response redirect issue.
The SAML Response was redirecting to "/" when it should have been redirecting to "/my/saml/endpoint".

Spring Boot + Security OAuth2.0 Client with Custom Provider

I am creating a OAuth2.0 client for a custom OAuth2 provider in Spring Boot + Security (version 5) application.
Below is the application.properties which has all the configuration and there is no additional configuration class in my project.
spring.security.oauth2.client.registration.xxxxxxxxx.client-id=XXXXXXXXXX
spring.security.oauth2.client.registration.xxxxxxxxx.client-secret=XXXXXXXXXX
spring.security.oauth2.client.registration.xxxxxxxxx.scope=openid
spring.security.oauth2.client.registration.xxxxxxxxx.redirect-uri-template=http://localhost:8080/login/oauth2/code/xxxxxxxxx
spring.security.oauth2.client.registration.xxxxxxxxx.client-name=xxxxxxxxx
spring.security.oauth2.client.registration.xxxxxxxxx.provider=xxxxxxxxx
spring.security.oauth2.client.registration.xxxxxxxxx.client-authentication-method=basic
spring.security.oauth2.client.registration.xxxxxxxxx.authorization-grant-type=authorization_code
spring.security.oauth2.client.provider.xxxxxxxxx.authorization-uri=https://api.xxxxxxxxx.com/authorize
spring.security.oauth2.client.provider.xxxxxxxxx.token-uri=https://api.xxxxxxxxx.com/token
spring.security.oauth2.client.provider.xxxxxxxxx.user-info-uri=https://api.xxxxxxxxx.com/userinfo?schema=openid
spring.security.oauth2.client.provider.xxxxxxxxx.user-name-attribute=name
spring.security.oauth2.client.provider.xxxxxxxxx.user-info-authentication-method=header
When i hit http://localhost:8080/ it redirects properly to provider's login page and after successful login it redirects back to my application.
Now the problem is when it redirects then it shows below error message.
I have googled for this error but didn't get any proper answer. Also, the OAuth2 provider didn't share such URL.
After research I came to know that i need to set below property. Should it be provided by Auth Provider?
spring.security.oauth2.client.provider.pepstores.jwk-set-uri
What exactly I am missing here in configuration?
Finally, the problem is solved. I just need to configure the jwk URI which should be provided by the Auth provider.
Below the final configuration for customer Auth Provider.
spring.security.oauth2.client.registration.xxxxxxxxx.client-id=XXXXXXXXXX
spring.security.oauth2.client.registration.xxxxxxxxx.client-secret=XXXXXXXXXX
spring.security.oauth2.client.registration.xxxxxxxxx.scope=openid
spring.security.oauth2.client.registration.xxxxxxxxx.redirect-uri-template=http://localhost:8080/login/oauth2/code/xxxxxxxxx
spring.security.oauth2.client.registration.xxxxxxxxx.client-name=xxxxxxxxx
spring.security.oauth2.client.registration.xxxxxxxxx.provider=xxxxxxxxx
spring.security.oauth2.client.registration.xxxxxxxxx.client-authentication-method=basic
spring.security.oauth2.client.registration.xxxxxxxxx.authorization-grant-type=authorization_code
spring.security.oauth2.client.provider.xxxxxxxxx.authorization-uri=https://api.xxxxxxxxx.com/authorize
spring.security.oauth2.client.provider.xxxxxxxxx.token-uri=https://api.xxxxxxxxx.com/token
spring.security.oauth2.client.provider.xxxxxxxxx.user-info-uri=https://api.xxxxxxxxx.com/userinfo?schema=openid
spring.security.oauth2.client.provider.xxxxxxxxx.user-name-attribute=name
spring.security.oauth2.client.provider.xxxxxxxxx.user-info-authentication-method=header
spring.security.oauth2.client.provider.xxxxxxxxx.jwk-set-uri=https://api.xxxxxxxxx.com/jwks
Thanks
When you receive JWT in client application, you need to verify the signature of JWT. To verify the signature you need public key of Auth provider. As per OAuth specifications, Auth provider can expose the public key through a URI and client can use this URI to get the public key to validate the JWT. This is what is missing in your configuration.

Spring Oauth2 : How to get all clientIds?

Has there anyway to retrieve all clientIds from OAuth2Authentication ?
I can get single clientId from Oauth2Request as
String clientId = auth.getOAuth2Request().getClientId();
but I'd like to know them all.
The client IDs are configured on the authorization server and there is no standard way to get the client IDs on resource server side.
Hence, if you use an external authorization server like Google, Facebook or GitHub you have to look into their APIs.
With your own authorization server, you could get all client IDs, see ClientRegistrationService:
Interface for client registration, handling add, update and remove of ClientDetails from an Authorization Server.
All you need is a ClientDetailsService implementing ClientRegistrationService, for example JdbcClientDetailsService.

Vulnerabilities when using client credentials and OWIN Middleware OAuth

I've implemented OAuth2 client_credentials flow in our MVC app. Our MVC app is actually the Resource in this scenario. I had much difficulty in securing a sample for this specific use case, since this flow is primarily used for API access, but I did it nonetheless.
I'd like to share some of the implementation details with you to ask for any information regarding vulnerabilities that I may be unaware of. I am in no way a security expert, which is what brought me here.
In .NET Framework 4.5.2 I used the Microsoft.Owin libraries v3.0.1. I know there are newer ways to set up this sort of thing, (.NET Core and IdentityServer4 for example), but as I said I was having difficulty finding a viable sample for this specific use case, so I did the best I could.
I implemented a Provider:
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
private ClientService clientService;
public ApplicationOAuthProvider()
{
this.clientService = new ClientService();
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId;
string clientSecret;
context.TryGetFormCredentials(out clientId, out clientSecret);
if (clientId == "XXXX" && clientSecret == "XXXXX")
{
context.Validated(clientId);
}
return base.ValidateClientAuthentication(context);
}
public override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
{
var client = clientService.GetClient(context.ClientId);
var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, client.ClientName));
var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
context.Validated(ticket);
//context.OwinContext.Response.Headers.Add("Access-Control-All‌​ow-Origin", new[] { "*" });
return base.GrantClientCredentials(context);
}
with the following startup code:
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
static Startup()
{
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(60),
//AllowInsecureHttp = true,
AuthenticationMode = AuthenticationMode.Active,
};
}
public void ConfigureAuth(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll)
.UseOAuthBearerTokens(OAuthOptions);
//app.UseOAuthBearerTokens(OAuthOptions);
}
}
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
ConfigureAuth(app);
}
}
and then created a Client app that is also a website and eventually gained access to the Resource (MVC app). The Resource has no users, therefore no login screen. The Resource (now) has a token endpoint. The Client app makes a request to the token endpoint with their credentials and, (after being authenticated), then uses that token in subsequent requests to the Resource.
I've found 2 different ways of using this token to gain access.
Include the access token in the request header. OR
Include the access token as a form parameter
My questions are concerning vulnerabilities to this scenario:
Assuming all communications between the client app and the server are happening over secure channels (https) and the client app is able to maintain the credentials in a secure fashion, what are the chances that an access token could be obtained or intercepted? Or, are there methods included in this flow (or perhaps another OAuth flow) that also include client verification? I realize that the client is already being authenticated via client_id/client_secret, but when I ask about verification, I'm asking about the origin of the request (assuming of course, that the method of verification does not include checking something that can be spoofed by a malicious user).
Is there an additional step of verification that I should be including that I may have missed - because there's alot of information out there and I did my best to scour, but I can't claim that I have a solid understanding of everything I've read so far.
If there is an additional verification step that I have missed, how does that fit into this (client_credentials) flow?
Thanks,
Carrie
I've found 2 different ways of using this token to gain access.
Include the access token in the request header. OR
Include the access token as a form parameter
This means that your access token is of type Bearer token (therefore, you can also use a third way to send the access token: using GET method with a query parameter).
what are the chances that an access token could be obtained or intercepted?
Or, are there methods included in this flow (or perhaps another OAuth flow)
that also include client verification?
You have a Bearer token, thus the RFC-6750 applies. The Threat Mitigation section answers your questions:
first, your access token may be disclosed if the version of TLS between your client app and the authorization server (to get the token), and between the client app and the resource server (to give the token), have a security flaw (excerpt: This requires that the communication interaction between the client and the authorization server, as well as the interaction between the client and the resource server, utilize confidentiality and integrity protection. Since TLS is mandatory to implement and to use with this specification, it is the preferred approach for preventing token disclosure via the communication channel.)
secondly, another way for your access token to be disclosed is when using a TLS accelerator. As said in the same section of the RFC: In some deployments, including those utilizing load balancers, the TLS connection to the resource server terminates prior to the actual server that provides the resource. This could leave the token unprotected between the front-end server where the TLS connection terminates and the back-end server that provides the resource.
There are other ways to get the access token disclosed.
The solution is not to implement another OAuth flow but to apply the recommandations in section 5.3 of the RFC. As a summary, the main recommandations are:
always use TLS,
validate TLS certificate chains,
use token encryption in addition to the usage of TLS protection: for instance, encrypt the token with a shared secret between the client app and the resource server,
don't store bearer tokens in cookies,
issue short-lived bearer tokens,
issue scoped bearer tokens (use audience restriction).
This is not in the RFC but I would add this recommandation: use mutual authentication. This means that the client app must have a X.509 certificate that must be verified by the resource server. This is possible in the specific Oauth2 flow you have chosen, because the client app is known by the resource server (with some alternative flows, it can not be done).

Resources