RespondToAuthChallengeResult.getAuthenticationResult() return null - java

I try to implement auth through AWS Cognito using aws-java-sdk-cognitoidentity and for convenience use cognitoauth classes
Authentication works fine but when I get AuthenticationResultType from respondToAuthChallengeResult it return null why that happens?
Sample:
AWSCryptoSettings cryptoParams = new AWSCryptoSettings();
AWSCognitoSession clientSession = new AWSCognitoSession(
cryptoParams, "test", "123456", USER_POOL_ID);
AWSCognitoIdentityProvider provider = AWSCognitoIdentityProviderClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(new AnonymousAWSCredentials()))
.withRegion(Regions.EU_WEST_1)
.build();
InitiateAuthRequest authRequest = new InitiateAuthRequest()
.withAuthFlow(AuthFlowType.USER_SRP_AUTH)
.withClientId(CLIENT_APP_ID)
.withAuthParameters(clientSession.step1()); //step1() return Map<String,String> with parameters for auth in it
//Respond to authentication challenge
InitiateAuthResult authResult = provider.initiateAuth(authRequest);
Map<String, String> params = authResult.getChallengeParameters();
Map<String, String> srpAuthResponses = clientSession.step2(params); //step2() return also Map<String, String> with formatted parameters.
RespondToAuthChallengeRequest respondToAuthChallengeRequest = new RespondToAuthChallengeRequest()
.withChallengeName(authResult.getChallengeName())
.withClientId(CLIENT_APP_ID)
.withChallengeResponses(srpAuthResponses);
RespondToAuthChallengeResult respondToAuthChallengeResult = provider.respondToAuthChallenge(respondToAuthChallengeRequest);
//debug
System.out.println(respondToAuthChallengeResult.getChallengeName());
System.out.println(respondToAuthChallengeResult.getChallengeParameters());
System.out.println(respondToAuthChallengeResult.getAuthenticationResult());
AuthenticationResultType authenticationResultType = respondToAuthChallengeResult.getAuthenticationResult(); //there null is retruned;
Output from sout is:
NEW_PASSWORD_REQUIRED
{userAttributes={"email":"username123#example.com"}, requiredAttributes=[]}
null
I don't configure correctly user pool or something is wrong in code?
Thanks.

This seems like you have created the user through the console or through the AdminCreateUser API. The user needs to create a new password as the state says.

Related

Generating authentication token for APNS connection

I am new to vertx, and using an app server created via vertx for connection with APNS for push notifications.
I created an authProvider via
private JWTAuth createOauthProvider() {
PubSecKeyOptions pubSecKeyOptions = new PubSecKeyOptions()
.setAlgorithm("ES256").setSecretKey(*/private key from .p8 extracted as is removing unwanted part in base64*/);
return JWTAuth.create(vertx(), new JWTAuthOptions().addPubSecKey(pubSecKeyOptions));
}
and then a JWT via
private String createJwt() {
Instant now = Instant.now();
JsonObject decoded = new JsonObject()
.put("alg", "ES256")
.put("kid", "kid here")
.put("iss", "teamid here")
.put("iat", now.getEpochSecond());
String jwt = oauth2JWTProvider.generateToken(decoded,
new JWTOptions().setAlgorithm("ES256"));
return jwt;
}
Now here's my query -
Is my token creation criteria correct?
JWT created after this when send to APNS returns a 403-InvalidProviderToken.
Appreciate all the help i can get. Thanks!!
Answering my own question as i was able to get around this and just in case it helps anyone else. Just posting the createJwt() method rest everything remains same-
private String createJwt() {
Instant now = Instant.now();
JsonObject header = new JsonObject()
.put(JwtConstants.ALG, "ES256")
.put(JwtConstants.KID, "kid here");
JsonObject payload = new JsonObject()
.put(JwtConstants.ISS, "team id here")
.put(JwtConstants.IAT, now.getEpochSecond());
String jwt = oauth2JWTProvider.generateToken(payload,
new JWTOptions()
.setHeader(header)
.setAlgorithm("ES256"));
return jwt;
}

Bad request with RestTemplate -> postForObject (Spring Boot)

I'm experiencing some troubles with a simple matter.
I'm trying to send a request to other REST service
//getting restTemplate from RestTemplateBuilder.build()
//endpoint and rest of variables came in properties
Map<String, String> map = new HashMap<>();
map.put("app", app);
map.put("username", username);
map.put("password", password);
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
String token = restTemplate.postForObject(loginEndpoint, headers, String.class, map);
And I recive:
Unexpected error occurred in scheduled task.
org.springframework.web.client.HttpClientErrorException: 400 Bad Request
The weird thing, when I use a simple CURL call and works smooth.
Already checked the variables and endpoint, and it's correct.
In this case, endpoint must have appropiate placeholders on end point url.
I made this method to do it easy:
private String placeHolders(Map<String, String> values){
String response = "?";
boolean first = true;
for(Map.Entry<String, String> entry:values.entrySet()){
if(first){
first = false;
}else{
response+="&";
}
response+=entry.getKey()+"="+entry.getValue();
}
return response;
}
And the call now Is:
String token = restTemplate.postForObject(loginEndpoint+placeHolders, headers, String.class, map);

Mocking authentication in Spring: List of providers throws NullPointerException

I'm trying to mock authentication in Java for an authentication. This is my test class for mocking the code:
MockitoAnnotations.initMocks(this);
mvc = MockMvcBuilders.standaloneSetup(this.controller).build();
final List<AuthenticationProvider> providers = mock(ArrayList.class);
final AbstractUserDetailsAuthenticationProvider provider = mock(
AbstractUserDetailsAuthenticationProvider.class);
when(provider.supports(any(Class.class))).thenReturn(false);
when(providers.size()).thenReturn(1);
session = new MockHttpSession();
when(providers.get(anyInt())).thenReturn(provider);
when(request.getSession()).thenReturn(session);
when(request.getSession(false)).thenReturn(session);
when(providers.iterator()).thenReturn(new Iterator<AuthenticationProvider>() {
private int currentIndex = 0;
#Override
public AuthenticationProvider next() {
return providers.get(currentIndex++);
}
#Override
public boolean hasNext() {
return currentIndex < providers.size() && providers.get(currentIndex) != null;
}
});
SingleProviderAuthenticationManager manager = new SingleProviderAuthenticationManager(providers);
Map<String, AuthenticationManager> map = new HashMap<String, AuthenticationManager>();
map.put("db", manager);
filter.setAuthenticationManagerMap(map);
when(request.getMethod()).thenReturn("POST");
when(request.getParameter("username")).thenReturn("admin");
when(request.getParameter("password")).thenReturn("admin");
List<User> users = new ArrayList<User>();
User user = new User();
user.setSourceSystem("db");
users.add(user);
when(userService.getUserReferenceByUsername("admin")).thenReturn(users);
auth = filter.attemptAuthentication(request, response);
Now, on the line where I put the manager in the map.put() method, when I put "db" it actually gives the provider manager as null and I get the NullPointerException in the ProviderManager.
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue;
}
Even though I've tested the same thing in my main code with the providers I pass its still showing a NullPointerException. And if I put "ldap" instead it gives me a NullPointerException in the UsernamePasswordAuthenticationFilter here:
(last line where the return happens)
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
I am stuck here, both these things when I pass them are not at all null. Any help?
Don't mock ArrayList instead create new array list and send it to actual class like below :
final List<AuthenticationProvider> providers = new ArrayList<>();
providers.add("AuthenticationProvider object values");
// when(providers.size()).thenReturn(1); -- no need of doing this stuff if you create direct arraylist object.
PS- If you are working with collections, do prefer creating new objects and send that object to actual class instead of mocking and sending, that is the best practice.
Hope it's useful.

Get facebook access token with spring social FacebookConnectionFactory

I am trying to implement a Facebook Connection using Spring Social, based on this example(from spring social manual):
FacebookConnectionFactory connectionFactory =
new FacebookConnectionFactory("clientId", "clientSecret");
OAuth2Operations oauthOperations = connectionFactory.getOAuthOperations();
OAuth2Parameters params = new OAuth2Parameters();
params.setRedirectUri("https://my-callback-url");
String authorizeUrl = oauthOperations.buildAuthorizeUrl(GrantType.AUTHORIZATION_CODE, params);
response.sendRedirect(authorizeUrl);
// upon receiving the callback from the provider:
AccessGrant accessGrant = oauthOperations.exchangeForAccess(authorizationCode, "https://my-callback-url", null);
Connection<Facebook> connection = connectionFactory.createConnection(accessGrant);
My problem is that I don't know what my redirect url should be.My code is this:
#RequestMapping("/face")
public String communicate() {
FacebookConnectionFactory connectionFactory =
new FacebookConnectionFactory(clientId, clientSecret);
OAuth2Operations oauthOperations = connectionFactory.getOAuthOperations();
OAuth2Parameters params = new OAuth2Parameters();
//this remdirectUri should be another one?
params.setRedirectUri("http://dev01.spring:8080/spring/face");
String authorizeUrl = oauthOperations.buildAuthorizeUrl(GrantType.AUTHORIZATION_CODE, params);
System.out.println(authorizeUrl);
//return "redirect:"+authorizeUrl;
// upon receiving the callback from the provider:
//AccessGrant accessGrant = oauthOperations.exchangeForAccess(authorizationCode, "https://my-callback-url", null);
//Connection<Facebook> connection = connectionFactory.createConnection(accessGrant);
}
My authorizeUrl is like this(from System.out line):
https://graph.facebook.com/oauth/authorize?client_id=myId&response_type=code&redirect_uri=http%3A%2F%2Fdev01.spring%3A8080%2Fspring%2Fface
If i uncomment the line where I continue the Oauth flow redirecting to this authorizeUrl, i'm getting the following error: This webpage has a redirect loop.
So my question is, what the redirect uri should be.Thank you.
Very late edit, in the hopes it helps someone. This is my controller and the method that does the whole Oauth2 dance. I must add that this worked when the question was asked, I have no idea how it behaves now.
#Controller
public class FacebookController {
private static final String clientId = "clientIdHere"; // clientId from facebook app
private static final String clientSecret = "clientSecret here"; // clientSecret
private FacebookConnectionFactory connectionFactory; // facebookConnectionFactory
/*
* If an authorization was given by provider(code) we get an token and bind the api.
*/
#RequestMapping("/facebook/callback")
public String authorize(#RequestParam("code") String authorizationCode,Model model) {
// exchange facebook code with an access token.
AccessGrant accessGrant = connectionFactory.getOAuthOperations().exchangeForAccess(authorizationCode, "http://localhost:8080/testApp/facebook/callback", null); // not that the application was deployed at "http://localhost:8080/testApp"
// connect to facebook with the given token.
Connection<Facebook> connection = connectionFactory.createConnection(accessGrant);
// bind the api
Facebook facebook = connection.getApi();
// get user profile informations
FacebookProfile userProfile = facebook.userOperations().getUserProfile();
// At this point you have acces to the facebook api.
// For ex you can get data about the user profile like this
// create user with facebook's user accounts details.
User facebookUser = new User(userProfile.getFirstName(),
userProfile.getLastName(),
userProfile.getEmail(),
Role.ROLE_FACEBOOKUSER,
"socialuser");
return "redirect:/home";
}
}

Spring OAuth2 - Manually creating an access token in the token store

I have a situation where I would like to create an access token myself (so not through the usual process). I have come up with something like this:
#Inject
private DefaultTokenServices defaultTokenServices;
...
OAuth2Authentication auth = xxx;
OAuth2AccessToken token = defaultTokenServices.createAccessToken(auth);
The only problem is that I am not sure how to create the OAuth2Authentication (in my code the part with xxx). I have the user & client info and I know which Authorities I want to grant this token.
Here it is, your use case may differ slightly based on the flow you are using. This is what works for a password grant flow. There are a few custom class like token store, token enhancer ect. but that is really just extended versions of the spring classes modified for our own needs.
HashMap<String, String> authorizationParameters = new HashMap<String, String>();
authorizationParameters.put("scope", "read");
authorizationParameters.put("username", "mobile_client");
authorizationParameters.put("client_id", "mobile-client");
authorizationParameters.put("grant", "password");
DefaultAuthorizationRequest authorizationRequest = new DefaultAuthorizationRequest(authorizationParameters);
authorizationRequest.setApproved(true);
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_UNTRUSTED_CLIENT"));
authorizationRequest.setAuthorities(authorities);
HashSet<String> resourceIds = new HashSet<String>();
resourceIds.add("mobile-public");
authorizationRequest.setResourceIds(resourceIds);
// Create principal and auth token
User userPrincipal = new User(user.getUserID(), "", true, true, true, true, authorities);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userPrincipal, null, authorities) ;
OAuth2Authentication authenticationRequest = new OAuth2Authentication(authorizationRequest, authenticationToken);
authenticationRequest.setAuthenticated(true);
CustomTokenStore tokenStore = new CustomTokenStore();
// Token Enhancer
CustomTokenEnhancer tokenEnhancer = new CustomTokenEnhancer(user.getUserID());
CustomTokenServices tokenServices = new CustomTokenServices();
tokenServices.setTokenEnhancer(tokenEnhancer);
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(tokenStore);
OAuth2AccessToken accessToken = tokenServices.createAccessTokenForUser(authenticationRequest, user);
Here is how to generate a Token using the TokenEndpoint interface (used to expose REST service) :
#Inject
private TokenEndpoint tokenEndpoint;
public ResponseEntity<?> getToken(Principal principal) {
HashMap<String, String> parameters = new HashMap<String, String>();
parameters.put("client_id", "appid");
parameters.put("client_secret", "myOAuthSecret");
parameters.put("grant_type", "password");
parameters.put("password", myUser.getPassword());
parameters.put("scope", "read write");
parameters.put("username", myUser.getLogin());
return tokenEndpoint.getAccessToken(principal, parameters);
}
Other way, to manually generate an OAuth2 Accesss Token we can use an instance of TokenService
#Autowired
private AuthorizationServerEndpointsConfiguration configuration;
#Override
public String generateOAuth2AccessToken(User user, List<Role> roles, List<String> scopes) {
Map<String, String> requestParameters = new HashMap<String, String>();
Map<String, Serializable> extensionProperties = new HashMap<String, Serializable>();
boolean approved = true;
Set<String> responseTypes = new HashSet<String>();
responseTypes.add("code");
// Authorities
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for(Role role: roles)
authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName()));
OAuth2Request oauth2Request = new OAuth2Request(requestParameters, "clientIdTest", authorities, approved, new HashSet<String>(scopes), new HashSet<String>(Arrays.asList("resourceIdTest")), null, responseTypes, extensionProperties);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(), "N/A", authorities);
OAuth2Authentication auth = new OAuth2Authentication(oauth2Request, authenticationToken);
AuthorizationServerTokenServices tokenService = configuration.getEndpointsConfigurer().getTokenServices();
OAuth2AccessToken token = tokenService.createAccessToken(auth);
return token.getValue();
}
I based my solution on Mop So's answer but instead of using:
return tokenEndpoint.getAccessToken(principal, parameters);
I used:
tokenEndpoint.postAccessToken(principal, parameters);
Why? Because if you use tokenEndpoint.getAccessToken(principal, parameters) the endpoing will throw you a HttpRequestMethodNotSupportedException because it has not been called with a GET method. At least, this is what happened to me with spring-security-oauth2-2.0.13.RELEASE
public OAuth2AccessToken getAccessToken() throws HttpRequestMethodNotSupportedException {
HashMap<String, String> parameters = new HashMap<>();
parameters.put("client_id", CLIENT_ID);
parameters.put("client_secret", CLIENT_SECRET);
parameters.put("grant_type", "client_credentials");
ClientDetails clientDetails = clientDetailsStore.get(CLIENT_ID);
// Create principal and auth token
User userPrincipal = new User(CLIENT_ID, CLIENT_SECRET, true, true, true, true, clientDetails.getAuthorities());
UsernamePasswordAuthenticationToken principal = new UsernamePasswordAuthenticationToken(userPrincipal, CLIENT_SECRET,
clientDetails.getAuthorities());
ResponseEntity<OAuth2AccessToken> accessToken = tokenEndpoint.postAccessToken(principal, parameters);
return accessToken.getBody();
}
This has worked for me:
#Override public OAuth2AccessToken getToken(String username, String password) {
HashMap<String, String> parameters = new HashMap<String, String>();
parameters.put("client_id", clientid);
parameters.put("grant_type", "password");
parameters.put("password", username);
parameters.put("scope", scope);
parameters.put("username", password);
AuthorizationRequest authorizationRequest = defaultOAuth2RequestFactory.createAuthorizationRequest(parameters);
authorizationRequest.setApproved(true);
OAuth2Request oauth2Request = defaultOAuth2RequestFactory.createOAuth2Request(authorizationRequest);
// Create principal and auth token
final UsernamePasswordAuthenticationToken loginToken = new UsernamePasswordAuthenticationToken(
username, password);
Authentication authentication = authenticationManager.authenticate(loginToken);
OAuth2Authentication authenticationRequest = new OAuth2Authentication(oauth2Request, authentication);
authenticationRequest.setAuthenticated(true);
OAuth2AccessToken accessToken = tokenServices.createAccessToken(authenticationRequest);
return accessToken;
}
In the Oauth2Configuration:
#Bean
DefaultOAuth2RequestFactory defaultOAuth2RequestFactory() {
return new DefaultOAuth2RequestFactory(clientDetailsService);
}
The rest of the Oauth2Configuration should look like in the article:
http://stytex.de/blog/2016/02/01/spring-cloud-security-with-oauth2/
Problem
I had problems with all the implementations listed here, so I finally managed to get my own with a stateless server, oauth2 and google social. Its just the last part of the tutorial that is missing here
The problem for me is that after executing the google oauth, I need to exchange a 10 second duration token for a long lived token. In order to do that I need to generate a JWT token and exchange it with a real access token generated by myself.
Implementation
#Service
class SocialTokenVerificationService {
#Autowired
private lateinit var jwsTokenService: JWSTokenService
#Autowired
private lateinit var clientDetailsService: ClientDetailsService
#Autowired
private lateinit var userService: UserService
#Autowired
private lateinit var tokenServices: DefaultTokenServices
#Autowired
private lateinit var tokenRequestFactory: OAuth2RequestFactory
fun verifyToken(token: String): OAuth2AccessToken? {
val claimSet = jwsTokenService.parseToken(token)
val userDetails = userService.loadUserByUsername(claimSet.subject)
val client = clientDetailsService.loadClientByClientId(DEFAULT_SERVER_CLIENT)
val parameters = HashMap<String, String>()
val authentication = UsernamePasswordAuthenticationToken(userDetails, null, userDetails.authorities)
return tokenServices.createAccessToken(OAuth2Authentication(
tokenRequestFactory.createOAuth2Request(client, TokenRequest(parameters, client.clientId, listOf("read", "write"), "password")),
authentication
))
}
}
JWSTokenService: its a self implemented class that encodes and decodes the exchanging token between google oauth and mine.
ClientDetailsService: bean declared as as part of the authorization server. Comes from my database
override fun configure(clients: ClientDetailsServiceConfigurer) {
clients.jdbc(datasource)
}
UserService: just a user service that extends UserDetailsService to obtain my users from the database
DefaultTokenServices: implemented as a primary bean as follows
#Bean
#Primary
fun tokenServices(): DefaultTokenServices {
val defaultTokenServices = DefaultTokenServices()
defaultTokenServices.setTokenStore(tokenStore())
defaultTokenServices.setSupportRefreshToken(true)
defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter())
return defaultTokenServices
}
OAuth2RequestFactory: implemented as a bean as follows
#Bean
fun oauthRequestFactory(clientsDetails: ClientDetailsService): OAuth2RequestFactory {
return DefaultOAuth2RequestFactory(clientsDetails)
}
With all this dependencies, what I need to do to generate a token that gets stored into the database and follows the same flows as the other ones without providing a password is:
Parse the jws token and verify its validity
Load the user that was authenticated with google
Generate an Authentication using the UsernamePasswordAuthenticationToken class. This is the key part, call DefaultTokenServices#createAccessToken to obtain a new token. It needs some arguments to execute the request:
OAuth2Request: it can be created with the OAuth2RequestFactory
The Authentication created previously
We need to generate a TokenRequest with the client that is triggering this token request. In my case I have that hardcoded
Summary
So to recap how to create a token manually:
We need to ask the token services to give us a token
For that we need to provide the authentication details and a client who does the request
With those 2 we can obtain a new token and serve it normally
In a spring boot 2.2.2 project I'm using the following code to do a pasword flow server side:
I had to specify authorizedClientManager.setContextAttributesMapper since PasswordOAuth2AuthorizedClientProvider is expecting specific attributes in the context. Hope that helps.
Config (application.yaml):
spring:
security:
oauth2:
client:
provider:
yourOauthProvider:
user-info-uri: ...
authorization-uri: ...
token-uri: ...
registration:
regId:
clientId: ...
clientSecret: ...
provider: yourOauthProvider
authorization-grant-type: password
redirect-uri-template: "{baseUrl}/login/oauth2/code/{registrationId}"
scope:
Wiring:
#Configuration
public class Oauth2ClientConfig {
#Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.password()
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
authorizedClientManager.setContextAttributesMapper(r -> {
Map<String, Object> m = new HashMap<>();
m.put(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, r.getPrincipal().getPrincipal());
m.put(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, r.getPrincipal().getCredentials());
return m;
});
return authorizedClientManager;
}
}
Service:
class AuthService {
#Autowired
private OAuth2AuthorizedClientManager authorizedClientManager;
public OAuth2AccessToken authenticate(String user, String password) {
Authentication principal = new UsernamePasswordAuthenticationToken(
user,
password);
OAuth2AuthorizeRequest authorizeRequest =
OAuth2AuthorizeRequest.withClientRegistrationId("regId")
.principal(principal)
.build();
OAuth2AuthorizedClient authorizedClient =
this.authorizedClientManager.authorize(authorizeRequest);
return authorizedClient.getAccessToken();
}
}

Categories

Resources