Auth0 : how to retrieve app_metadata and user_metadata in token? - java

I try to authenticate a user with its username and password. I want to retrieve the JWT in response and find in it his permissions (stored in app_metadata).
But the id_token returned does not contain the user_metadata or app_metadata.
I tried with the Java driver and HTTP call.
Java :
AuthAPI auth = new AuthAPI("my-domain.auth0.com", "my_client_id", "my_secret_id");
AuthRequest request = auth.login(username, password)
.setScope("openid app_metadata user_metadata");
try {
TokenHolder holder = request.execute();
return holder;
} catch (Auth0Exception e) {
throw new AuthentException("Error authenticating " + username, e);
}
HTTP :
final String req = "{"
+ "\"username\":\"test#domain.com\","
+ "\"password\":\"test\","
+ "\"scope\":\"openid app_metadata user_metadata\","
+ "\"client_id\":\"my_client_id\","
+ "\"client_secret\":\"my_secret_id\","
+ "\"grant_type\":\"password\""
+ "}";
RestTemplate template = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(req, headers);
ResponseEntity<String> response = template.exchange("https://my-domain.auth0.com/oauth/token", HttpMethod.POST, entity, String.class);
The id_token returned contains only :
{
"email": "test#domain.com",
"email_verified": true,
"iss": "https://my-domain.auth0.com/",
"sub": "auth0|xxx",
"aud": "my_client_id",
"exp": 1497744462,
"iat": 1495116462
}
I tried to add a rule :
function (user, context, callback) {
var namespace = 'https://my-domain.auth0.com/';
if (context.idToken && user.user_metadata) {
context.idToken[namespace + 'user_metadata'] = user.user_metadata;
}
if (context.idToken && user.app_metadata) {
context.idToken[namespace + 'app_metadata'] = user.app_metadata;
}
callback(null, user, context);
}
And a hook :
module.exports = function(client, scope, audience, context, cb) {
var access_token = {};
access_token.scope = scope;
access_token.scope.push('user_profile');
cb(null, access_token);
};
But nothing adds the metadata to the id_token.
How could I retrieve these metadata ?
Thanks.

I found that the /oauth/ro endpoint is working : https://auth0.com/docs/api/authentication#resource-owner
final String req = "{"
+ "\"username\":\"ambre#domain.com\","
+ "\"password\":\"test\","
+ "\"scope\":\"" + settings.getScope() + "\","
+ "\"connection\":\"Username-Password-Authentication\","
+ "\"client_id\":\"" + settings.getClientId() + "\","
+ "\"grant_type\":\"password\""
+ "}";
RestTemplate template = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(req, headers);
ResponseEntity<String> response = template.exchange("https://my-domain.auth0.com/oauth/ro", HttpMethod.POST, entity, String.class);
But I can't find the equivalent in java driver 1.0.0

Related

401 unauthorized : [no body] with root cause in JAVA Spring

I'm creating my first REST API using JAVA Spring and when I'm making a rest call to an external API, I get
401 Unauthorized: [no body]
I think my problem is here:
requestParams.add("Grant_type", "client_credentials");
I saw some questions related to this but none well able to solve my problem.
Spring REST template - 401 Unauthorized error
Spring Boot Callable - 401 Unauthorized: [no body]
JAVA code:
public String getAuth(String client_id, String app_secret) {
String auth = client_id + ":" + app_secret;
return Base64.getEncoder().encodeToString(auth.getBytes());
}
#GetMapping(value = "/token")
public Object generateAccessToken() {
String auth = this.getAuth(
"CLIENT_ID",
"APP_SECRET"
);
RestTemplate restTemplate = new RestTemplate();
String base = "https://external-api.com";
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Basic " + auth);
MultiValueMap<String, String> requestParams = new LinkedMultiValueMap<>();
requestParams.add("Grant_type", "client_credentials");
ResponseEntity<Object> response = restTemplate.postForEntity(
base + "/v1/oauth2/token",
requestParams,
Object.class,
headers
);
return response.getBody();
}
Here's the solution to my own question.
This is what I had to change;
MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<>();
requestBody.add("grant_type", "client_credentials");
HttpEntity<?> request = new HttpEntity<>(requestBody, headers);
ResponseEntity<String> response = restTemplate.postForEntity(
base +"/v1/oauth2/token",
request,
String.class
);
Here's the final solution:
public String generateAccessToken() {
String base = "example-api.com";
String auth = this.getAuth(
"client id",
"app_id"
);
// create an instance of RestTemplate
RestTemplate restTemplate = new RestTemplate();
// create headers
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.set("Authorization", "Basic " + auth);
MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<>();
requestBody.add("grant_type", "client_credentials");
HttpEntity<?> request = new HttpEntity<>(requestBody, headers);
ResponseEntity<String> response = restTemplate.postForEntity(
base +"/v1/oauth2/token",
request,
String.class
);
// check response
if (response.getStatusCode() == HttpStatus.OK) {
System.out.println("Request Successful");
System.out.println(response.getBody());
} else {
System.out.println("Request Failed");
System.out.println(response.getStatusCode());
}
JSONObject object = new JSONObject(response.getBody());
return object.getString("access_token");
}

Java Exception: Unauthorized: 401

I'm getting the error: Exception: org.springframework.web.client.HttpClientErrorException$Unauthorized: 401, when trying to connect to Jira through HttpHeader, and the credentials are configured in a configserver file, which would be this:
#Component
public class JiraHttpHeadersHelper {
#Value("${first.jira.auth.user}")
private String firstJiraAuthUser;
#Value("${first.jira.auth.psw}")
private String firstJiraAuthPsw;
public HttpHeaders jiraHeadersWithAuthentication() {
String plainCreds = firstJiraAuthUser + ":" + firstJiraAuthPsw;
System.out.println("Credenciales JiraServices: "+plainCreds);
byte[] base64CredsBytes = Base64.getEncoder().encode(plainCreds.getBytes());
String base64Creds = new String(base64CredsBytes);
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic" + base64Creds);
headers.setContentType(MediaType.APPLICATION_JSON);
System.out.println("Authorization JiraServices: "+headers);
return headers;
}
}
And the method where I command to call the file above and where I get the error on the line ResponseEntity<String> result = restTemplate.exchange(url, HttpMethod.GET,this.requestEnt, String.class);, would be this:
public ResponseEntity<String> getPriorityJira() {
//Request entity created
this.requestEnt = new HttpEntity(this.jiraHttpHeadersHelper.jiraHeadersWithAuthentication());
String jql = "priority";
String url = jiraBaseURL + jql;
try {
ResponseEntity<String> result = restTemplate.exchange(url, HttpMethod.GET,this.requestEnt, String.class);
System.out.println("HttpStatus"+HttpStatus.OK);
if (result.getStatusCode() == HttpStatus.OK) {
return result;
} else {
logger.error("Jira Generic User maybe blocked, status from API: " +result.getStatusCode() + ". Body: "+ result.getBody());
return new ResponseEntity<>(result.getBody(), result.getStatusCode());
}
} catch(HttpClientErrorException e) {
logger.error("Error getting priorityJira. Exception: "+ e);
return new ResponseEntity<>(e.getStatusCode());
}
}
In fact, when I run the debug and check the credentials, it brings them up without a problem. I've already searched, tried most of the links on this page and I can't find the solution.
Any help would be appreciated in this case, thanks in advance.
When you define your authorization header you concat your key with « Basic » without adding a white space.
headers.add("Authorization", "Basic" + base64Creds);
Instead of :
headers.add("Authorization", "Basic " + base64Creds);
Maybe it’s just that.
Edit :
The answer was to add StandardCharsets.UTF-8 to the String constructor.

Proper way to get Oath2 access token and call another service in Java Spring Boot

I want to get the oath2 access token and using this I want to call an another service.
Below code does the same it gets the access token and call an another API using that. Using the below code I am able to do what ever I want with the below code.
But I am new to Spring Security I just want to know if there is a better way to do this. Like rather than making a separate call to get the token and then call the service can i do it in a single call? Or Using any other class provided by Spring can I write this in a better way ?
public class TestAPIToken{
#RequestMapping(value = "/showEmployees", method = RequestMethod.GET)
public ModelAndView showEmployees(#RequestParam("code") String code) throws JsonProcessingException, IOException {
String accessToken = getAccessToken();
System.out.println("API Token ---------" + accessToken);
HttpEntity<String> response = getResponseByCallingWithToken(accessToken);
System.out.println("API Response ---------" + response.getBody());
return null;
}
private HttpEntity<String> getResponseByCallingWithToken(String accessToken) {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.add("Authorization", "Bearer " + accessToken);
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url)
.queryParam("msisdn", msisdn)
.queryParam("email", email);
HttpEntity<?> entity = new HttpEntity<>(headers);
HttpEntity<String> response = restTemplate.exchange(
builder.toUriString(),
HttpMethod.GET,
entity,
String.class);
reponse.getBody();
return response;
}
private String getAccessToken() {
ResponseEntity<String> response = null;
System.out.println("Authorization Code------" + code);
RestTemplate restTemplate = new RestTemplate();
// According OAuth documentation we need to send the client id and secret key in the header for authentication
String encodedCredentials = new String(Base64.encodeBase64(credentials.getBytes()));
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.add("Authorization", "Basic " + encodedCredentials);
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("scope","scope,value");
body.add("grant_type","scope,value");
HttpEntity<String> request = new HttpEntity<String>(body, headers);
String access_token_url = "http://localhost:8080/oauth2/token";
ResponseEntity<TokenModel> response = restTemplate.exchange(access_token_url, HttpMethod.POST, request, TokenModel.class);
String accessToken = response.getBody().access_token;
return accessToken;
}
}
class TokenModel{
String access_token;
String scope;
String token_type;
String expires_in;
}
I am new to Spring security. Please help even if this seems simple to you
NB: This question does not have an exact duplicate

Cannot redirect after facebook login using response entity in spring boot

Ok, so I am trying to achieve facebook authentication in my application but after success user is not redirected to homepage even though location header is set.
This is my current scenario.
Front-End call:
#GetMapping(value = "/login/facebook")
public String loginFacebook() {
String facebookLoginUrl = String.valueOf(this.restTemplate.getForEntity(this.serverApi + "/login/facebook",String.class).getHeaders().get("Location"));
facebookLoginUrl = facebookLoginUrl.substring(1,facebookLoginUrl.length()-1);
System.out.println(facebookLoginUrl);
return "redirect:" + facebookLoginUrl;
}
Back-End:
#RequestMapping(value = "/login/facebook", method = RequestMethod.GET)
public String startFacebookProcess() {
this.facebookConnectionFactory = new FacebookConnectionFactory(appId,appSecret);
OAuth2Operations operations = this.facebookConnectionFactory.getOAuthOperations();
OAuth2Parameters parameters = new OAuth2Parameters();
// promeni url-a za front-enda
parameters.setRedirectUri("http://localhost:8080/login/returnFromFacebook");
parameters.setScope(this.scope);
System.out.println("In startFacebookProcess");
String url = operations.buildAuthorizeUrl(parameters);
System.out.println(url);
return "redirect:" + url;
}
After that, the user is being redirected to the official facebook login page where he allows his details to be used.
Then he performs a GET request to my Front-End with his authorization code:
#GetMapping(value = "/login/returnFromFacebook")
public ResponseEntity getFacebookData(#RequestParam("code") String authorizationCode){
System.out.println(authorizationCode);
ResponseEntity response = this.restTemplate.getForEntity(this.serverApi +
"/login/returnFromFacebook?code=" + authorizationCode, ResponseEntity.class);
if(response.getStatusCode().is2xxSuccessful()){
return response;
}
return new ResponseEntity(HttpStatus.CONFLICT);
}
My Back-End is being called and I contact facebook to fetch the user data using his authorization code. Then I return a ResponseEntity which inherited all the HttpServletResponse headers and cookies.
#RequestMapping(value = "/login/returnFromFacebook", method = RequestMethod.GET)
public ResponseEntity getDataFromFacebook(#RequestParam("code") String authorizationCode,
HttpServletResponse response) throws IOException {
// leg 2
OAuth2Operations operations = this.facebookConnectionFactory.getOAuthOperations();
AccessGrant accessGrant = operations.exchangeForAccess(authorizationCode,
"http://localhost:8080/login/returnFromFacebook",null);
// leg 3
System.out.println("code: " + authorizationCode);
Connection<Facebook> connection = this.facebookConnectionFactory.createConnection(accessGrant);
User currentUser = connection.getApi().fetchObject("me", User.class,"email,first_name,last_name");
System.out.println("Email: " + currentUser.getEmail());
if(this.appUserDAO.findUserAccountByEmail(currentUser.getEmail()) == null){
Map<String, String> facebookDetailsMap = new LinkedHashMap<>();
facebookDetailsMap.put("email",currentUser.getEmail());
facebookDetailsMap.put("name",currentUser.getFirstName() + " " + currentUser.getLastName());
this.appUserDAO.saveFacebookAccount(facebookDetailsMap);
}
// Create JWT Token
String token = JWT.create()
.withSubject(currentUser.getEmail())
.withExpiresAt(new Date(System.currentTimeMillis() + JwtProperties.EXPIRATION_TIME))
.sign(HMAC512(JwtProperties.SECRET.getBytes()));
JwtAuthenticationFilter.setJwtCookie(response,token);
System.out.println(response.getStatus() + " " + response.getHeader("set-cookie"));
response.setHeader("Location", "http://localhost:8080/");
// HttpHeaders headers = new HttpHeaders();
// headers.add(index");
return new ResponseEntity<>(HttpStatus.CREATED);
}
This is the received response header in the browser. The cookies are set and everything but user is not being redirected to Location header. :
I tried to return a String "redirect:/" from GetFacebookData(Front-End) but the response cookies and headers are not being applied. I don't know why the Location header is not working.
I fixed it by returning the 'index' template if the back-end call was sucessful but also apply the cookie header into the original response (which was to the front-end) using HttpServletResponse (this is not possible with ResponseEntity btw). With HttpServletResponse you dont have to return it to the browser, it applies everything by default. With ResponseEntity you have to return ResponseEntity Object in order to apply headers. This is how the function looks like now:
#GetMapping(value = "/login/returnFromFacebook")
public String getFacebookData(#RequestParam("code") String authorizationCode, HttpServletResponse httpServletResponse) throws IOException {
System.out.println(authorizationCode);
ResponseEntity response = this.restTemplate.getForEntity(this.serverApi +
"/login/returnFromFacebook?code=" + authorizationCode, ResponseEntity.class);
System.out.println(response.getStatusCodeValue() + " " + response.getHeaders().get("Location"));
//set cookie for original request
String jwtCookieHeader = String.valueOf(response.getHeaders().get("set-cookie"));
jwtCookieHeader = jwtCookieHeader.substring(1,jwtCookieHeader.length()-1);
httpServletResponse.setHeader("set-cookie", jwtCookieHeader);
if(response.getStatusCode().is2xxSuccessful()){
return "redirect:/";
}
return String.valueOf(new ResponseEntity(HttpStatus.CONFLICT));
}

Rest Api call gives error 400 using Spring Oauth2

I'm building a rest API using Spring security Oauth2 to secure it.
The following curl command runs succesfully and I get the token:
curl -X POST -vu clientapp:123456 http://localhost:8080/dms-application-0.0.1-SNAPSHOT/oauth/token -H "Accept: application/json" -d "password=spring&username=roy&grant_type=password&scope=read%20write&client_secret=123456&client_id=clientapp"
The following test to get the token also runs succesfully:
#Test
public void getAccessToken() throws Exception {
String authorization = "Basic " + new String(Base64Utils.encode("clientapp:123456".getBytes()));
String contentType = MediaType.APPLICATION_JSON + ";charset=UTF-8";
// #formatter:off
String content = mvc
.perform(
post("/oauth/token")
.header("Authorization", authorization)
.contentType(
MediaType.APPLICATION_FORM_URLENCODED)
.param("username", "roy")
.param("password", "spring")
.param("grant_type", "password")
.param("scope", "read write")
.param("client_id", "clientapp")
.param("client_secret", "123456"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.access_token", is(notNullValue())))
.andExpect(jsonPath("$.token_type", is(equalTo("bearer"))))
.andExpect(jsonPath("$.refresh_token", is(notNullValue())))
.andExpect(jsonPath("$.expires_in", is(greaterThan(4000))))
.andExpect(jsonPath("$.scope", is(equalTo("read write"))))
.andReturn().getResponse().getContentAsString();
// #formatter:on
String token= content.substring(17, 53);
}
However, when calling the rest end point externally from a webapp using Spring RestTemplate gives me a http error 400.
Below the code:
#RequestMapping(value = "/authentication", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity authenticate(#RequestBody CredentialsDto credentials) {
try {
String email = credentials.getEmail();
String password = credentials.getPassword();
String tokenUrl = "http://" + env.getProperty("server.host") + ":8080" + "/dms-application-0.0.1-SNAPSHOT" + "/oauth/token";
// create request body
JSONObject request = new JSONObject();
request.put("username", "roy");
request.put("password", "spring");
request.put("grant_type","password");
request.put("scope","read write");
request.put("client_secret","123456");
request.put("client_id","clientapp");
// set headers
HttpHeaders headers = new HttpHeaders();
String authorization = "Basic " + new String(Base64Utils.encode("clientapp:123456".getBytes()));
String contentType = MediaType.APPLICATION_FORM_URLENCODED.toString();
headers.set("Authorization",authorization);
headers.set("Accept","application/json");
headers.set("Content-Type",contentType);
HttpEntity<String> entity = new HttpEntity<String>(request.toString(), headers);
// send request and parse result
ResponseEntity<String> loginResponse = restClient.exchange(tokenUrl, HttpMethod.POST, entity, String.class);
// restClient.postForEntity(tokenUrl,entity,String.class,)
if (loginResponse.getStatusCode() == HttpStatus.OK) {
//JSONObject userJson = new JSONObject(loginResponse.getBody());
String response = loginResponse.getBody();
return ResponseEntity.ok(response);
} else if (loginResponse.getStatusCode() == HttpStatus.UNAUTHORIZED) {
// nono... bad credentials
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
} catch (Exception e) {
e.printStackTrace();
return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
}
return null;
}
And the error I get:
"Missing grant type"
Any ideas of what can be wrong or any other ways to do it? Because I'm completely stuck on this.
Thank you
Try to do it like this:
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("username", "roy");
map.add("password", "spring");
map.add("grant_type", "password");
map.add("scope", "read write");
map.add("client_secret","123456");
map.add("client_id","clientapp");
HttpEntity request = new HttpEntity(map, headers);
One more thing, when you ask for a token make sure not to send a json request, but with this header:
headers.add("Content-Type", "application/x-www-form-urlencoded");

Categories