Spring OAuth2 Authentication object is null with CustomAuthenticationProvider - spring-security-oauth2

I am using Spring OAuth2 to protect my REST API's and implemented client_credentials, password grant types. They work perfectly fine except ResourceOwnerPassword flow with user credentials in the POST body.
Token endpoint call with the below URL is OK (If we send the user credentials as URL parameters)
HTTP POST: /oauth/token?grant_type=password&username=123&password=456
But with the below setup, my Authentication.getCredentials and Authentication.getPrincipal Objects are always empty.. I am using a custom authentication provider since I need to validate user against an external LDAP system.
Does this mean user credentials(Usernamd and password) can only be sent in the URL parameters to the token endpoint(/oauth/token)??
My configurations are:
Authorization server configuration:
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(
Arrays.asList(new CustomTokenEnhancer(), accessTokenConverter));
endpoints
.tokenStore(tokenStore).tokenEnhancer(tokenEnhancerChain)
.userApprovalHandler(userApprovalHandler)
.authenticationManager(userAuthenticationManager);
}
#Bean
AuthenticationManager userAuthenticationManager() {
List<AuthenticationProvider> authenticationProviders = new ArrayList<AuthenticationProvider>();
authenticationProviders.add(new CustomAuthenticationProvider()));
return new ProviderManager(authenticationProviders);
}
Custom Authentication provider:
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
System.out.println("authentication object........"+authentication);
String userName;
String password;
System.out.println("authentication object credentials........"+authentication.getCredentials());
userName = authentication.getName();
password = authentication.getCredentials().toString();
}
Authentication object printed in the console:
org.springframework.security.authentication.Usernam
ePasswordAuthenticationToken#7a2162f9: Principal: null; Credentials: [PROTECTED]
; Authenticated: false; Details: {grant_type=password}; Not granted any authorities
However, I did find a post where they said user credentials in the request body works with Token endpoint.. Here is the link about it..
https://github.com/spring-projects/spring-security-oauth/issues/260
As per the above it seems I am missing something..
Any help/hint would be appreciated..

Related

Spring Security form post authentication

I have a page to access with form post request (webview page for mobile apps). My application can't have a login form but I need to secure it. Mobile applications will call this webview page with authentication parameters such as (email/password). I need to call third-party api with given authentication parameters and decide it was authenticated or not. Which approach should I use for my scenarios ?
If it possible to pass authentication parameters in the Authorization header, you can enable http basic authentication in your application:
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
Otherwise, you can implement your own security filter to create a UsernamePasswordAuthenticationToken (or any other class implementing Authentication) instance from your specific authentication parameters and pass to AuthenticationManager; but in the case of another class, you need to make the authentication provider below support it by overriding the public boolean supports(Class<?> authentication) method.
Then implement a custom AuthenticationProvider that will delegate authentication to the third-party API, e.g.:
public class RestAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
private static final String AUTH_URL = "http://third-party-service/authentication/basic";
private RestTemplate restTemplate;
public RestAuthenticationProvider() {
this.restTemplate = new RestTemplate();
}
#Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
if (authentication.getCredentials() == null) {
this.logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
if (!authentication.getCredentials().toString().equals(userDetails.getPassword())) {
this.logger.debug("Authentication failed: invalid credentials");
throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
}
#Override
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) {
String password = authentication.getCredentials().toString();
try {
ResponseEntity<String> authenticationResponse = authenticate(username, password);
if (authenticationResponse.getStatusCode().value() == 401) {
throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
return createUser(authenticationResponse.getBody());
} catch (BadCredentialsException ex) {
throw ex;
} catch (Exception ex) {
throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
}
}
private ResponseEntity<String> authenticate(String username, String password) {
HttpEntity entity = new HttpEntity(createHeaders(username, password));
return restTemplate.exchange(AUTH_URL, HttpMethod.GET, entity, String.class);
}
private HttpHeaders createHeaders(String username, String password) {
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "application/json");
String authorization = username + ":" + password;
String basic = Base64.getEncoder().encodeToString(authorization.getBytes());
headers.set("Authorization", "Basic " + basic);
return headers;
}
private UserDetails createUser(String json) {
return null; // TODO: Implement
}
}
And finally, make Spring Security to use your provider:
#Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(new RestAuthenticationProvider())
.eraseCredentials(false);
}
I need to call third-party api with given authentication parameters
and decide it was authenticated or not
Assuming you need to use username / password to send to 3rd party, when you first login into the app, you can create a long lived token in back-end and sent it to the app to store in secure store. Later when you want to load the protected webview, send this token along with the request (in header or body via javascript) and in the server side pick the user / password corresponding to the token and authenticate with 3rd party.
This way you will never need to store password on client side and you can manually make the token at backend inactive too. If you like to go standard way, then you may take a look at Password Grant of OAuth 2 / OpenID Connect / . With the correct infrastructure in place you can get access tokens during login process and use that for your protected page. Spring Security has support for this flow - you can take a look here.

How to authenticate an access token using OWIN OAuthBearerAuthentication?

What I want:
A token generator use OAuthAuthorizationServer and token consumer use OAuthBearerAuthentication (authenticate the access token).
Use OWIN pipeline to manage all stuff, token stuff and web api stuff.
What about the code:
public void Configuration(IAppBuilder app)
{
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
AuthorizeEndpointPath = "/Authorize",
AllowInsecureHttp = true,
Provider = new OAuthAuthorizationServerProvider
{
OnGrantCustomExtension = GrantCustomExtension,
OnValidateClientRedirectUri = ValidateClientRedirectUri,
OnValidateClientAuthentication = ValidateClientAuthentication,
}
});
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
Provider = new OAuthBearerAuthenticationProvider
{
//Handles applying the authentication challenge to the response message.
ApplyChallenge=MyApplyChallenge,
//Handles processing OAuth bearer token.
RequestToken=MyRequestToken,
//Handles validating the identity produced from an OAuth bearer token.
ValidateIdentity = MyValidateIdentity,
}
});
app.UseWebApi(new WebApplication3.Config.MyWebApiConfiguration());
}
What's the question:
The 3 properties of OAuthBearerAuthenticationProvider,
ApplyChallenge, RequestToken and ValidateIdentity. How to
implement the 3 methods?
In the token authetication process, What I thought is to decrypt the access token, validate the token from the client, and if the token is validated, put the identities of the token to the HttpContext.Current.User.
The OAuthBearerAuthenticationProvider's responsibility is to fulfill the
previous steps. Am I right?
As you know, UseOAuthAuthorizationServer has the job of authenticating the user. Then, UseOAuthBearerAuthentication has the job of ensuring that only authenticated users can access your application. Often, these two jobs are assigned to different web application. It looks like your application is doing both.
There are certainly some cases were you need to override the default OAuthBearerAuthenticationProvider. Maybe you do, or maybe you don't In my case, ApplicationCookie didn't quite fit the scenario. So, I'm storing a 3rd party JWT token in a cookie, rather than the header, and using it to indicate that the user is authenticated to a web application. I also needed to redirect to my own login page, rather than provide a 401.
Here's an implementation that does both:
public class CustomOAuthBearerProvider : IOAuthBearerAuthenticationProvider
{
public Task ApplyChallenge(OAuthChallengeContext context)
{
context.Response.Redirect("/Account/Login");
return Task.FromResult<object>(null);
}
public Task RequestToken(OAuthRequestTokenContext context)
{
string token = context.Request.Cookies[SessionKey];
if (!string.IsNullOrEmpty(token))
{
context.Token = token;
}
return Task.FromResult<object>(null);
}
public Task ValidateIdentity(OAuthValidateIdentityContext context)
{
return Task.FromResult<object>(null);
}
}
I didn't need to do anything special in ValidateIdentity, but I needed to satisfy the interface.
To wire this up, tell your app to use JwtBearerAuthentication with your provider:
// controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AllowedAudiences = audiences.ToArray(),
IssuerSecurityTokenProviders = providers.ToArray(),
Provider = new CookieOAuthBearerProvider()
}
);

Asp.Net Identity - How to set Unauthorized programmatically?

I have a Web API 2 application which uses Asp.Net Identity for Authentication and Authorization. I also have a custom Message Handler to do an additional custom check to finalize the authentication (as well as parse some API data that is necessary to connect to the right schema on a multi-tenancy data store).
Here is my working code for the message handler:
public class AuthenticationHeadersHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.Headers.Contains("Authorization"))
{
// Authenticate
var auth = request.GetOwinContext().Authentication.AuthenticateAsync(OAuthDefaults.AuthenticationType);
// Get user ID from token
identityId = auth.Result.Identity.GetUserId();
// Please note, the oAuth token would have successfully authenticated by now
// ... Do some custom authentication and data gathering
if (failedCheck)
{
// If user fails checks, I would like to force Asp.Net Identity to
// return 401 not authorized here, or flag the request as not authorized
}
}
}
}
Considering the code above, how can I manually flag the request as unauthorized even if it passed the initial authentication?
I think this should work:
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.Headers.Contains("Authorization"))
{
// Authenticate
var auth = request.GetOwinContext().Authentication.AuthenticateAsync(OAuthDefaults.AuthenticationType);
// Get user ID from token
identityId = auth.Result.Identity.GetUserId();
// Please note, the oAuth token would have successfully authenticated by now
// ... Do some custom authentication and data gathering
if (failedCheck)
{
// Return 401 not authorized here.
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
}
}
// If we got here send an HTTP status of 200
return new HttpResponsMessage(HttpStatusCode.OK);
}

Programmatically log-in a user using spring security

The opposite of: How to manually log out a user with spring security?
In my app I have register new user screen, which posts to a controller which creates a new user within db (and does a few obvious checks).I then want this new user to be automatically logged in ... I kind of want somethign like this :
SecurityContextHolder.getContext().setPrincipal(MyNewUser);
Edit
Well I have almost implemented based on the answer to How to programmatically log user in with Spring Security 3.1
Authentication auth = new UsernamePasswordAuthenticationToken(MyNewUser, null);
SecurityContextHolder.getContext().setPrincipal(MyNewUser);
However, when deployed the jsp can not access my MyNewUser.getWhateverMethods() whereas it does when normal login procedure followed. the code that works nomrally, but throws an error when logged in like above is below :
<sec:authentication property="principal.firstname" />
In my controller i have this, which logs user in as normal :
Authentication auth =
new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(auth);
Where user is my custom user object(implementing UserDetails) that is newly created. The getAuthorities() method does this (just because all my users have the same role):
public Collection<GrantedAuthority> getAuthorities() {
//make everyone ROLE_USER
Collection<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
GrantedAuthority grantedAuthority = new GrantedAuthority() {
//anonymous inner type
public String getAuthority() {
return "ROLE_USER";
}
};
grantedAuthorities.add(grantedAuthority);
return grantedAuthorities;
}
You can also inject your spring security configured UserDetailsManager to your controller and use that to get the UserDetails which holds the principal and authorities to avoid duplicate code:
// inject
#Autowired
private UserDetailsManager manager;
// use in your method
UserDetails userDetails = manager.loadUserByUsername (token.getUsername ());
Authentication auth = new UsernamePasswordAuthenticationToken (userDetails.getUsername (),userDetails.getPassword (),userDetails.getAuthorities ());
SecurityContextHolder.getContext().setAuthentication(auth);
From the spring security source AbstractAuthenticationProcessingFilter:
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
Authentication authResult) throws IOException, ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
}
// you need this
SecurityContextHolder.getContext().setAuthentication(authResult);
rememberMeServices.loginSuccess(request, response, authResult);
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
}
successHandler.onAuthenticationSuccess(request, response, authResult);
}
Note however that the SecurityContextHolder is usually cleared upon completion of the filter chain.
For anyone trying to do this with Reactive Spring Security, this is what I did and it seemed to work.
private Mono<Authentication> authenticateUser(ServerWebExchange exchange, UserDetails userDetails,String rawPassword)
{
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userDetails.getUsername(),rawPassword);
return reactiveAuthenticationManager.authenticate(token).filter(auth -> auth.isAuthenticated()).flatMap(auth ->
{
SecurityContextImpl securityContext = new SecurityContextImpl();
securityContext.setAuthentication(auth);
return securityContextRepository.save(exchange,securityContext).then(Mono.just(auth));
});
}

Make Programmatic login without username/password?

Greetings all
i am using the following method to make programmatic login for the user, but with his username & password, and it works fine:
public static void autoLogin(User user, HttpServletRequest request,
AuthenticationManager authenticationManager) {
GrantedAuthority[] grantedAuthorities = new GrantedAuthority[] { new GrantedAuthorityImpl(
user.getAuthority()) };
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
user.getUserName(), user.getPassword(),
grantedAuthorities);
// generate session if one doesn't exist
request.getSession();
token.setDetails(new WebAuthenticationDetails(request));
Authentication authenticatedUser = authenticationManager
.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authenticatedUser);
// setting role to the session
request
.getSession()
.setAttribute(
HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
SecurityContextHolder.getContext());
}
and i was wondering if it's possible to make programmatic login but without the username or the password authentication, just makes this user authenticated.
You can create your own subclass of Authentication, implement an AuthenticationProvider that supports it and configure authentication manager to use this provider.
(Actually, you can simply put a custom Authentication that always returns true from isAuthenticated() into SecurityContext, but this approach bypasses AuthenticationManager, so, for example, AuthenticationSuccessEvent wouldn't be published).
managed to do it by removing those lines
token.setDetails(new WebAuthenticationDetails(request));
Authentication authenticatedUser = authenticationManager .authenticate(token);

Resources