im using javax.servlet.http.HttpServletRequest; to get the request in java controller, i just want to create controller that returns actual user name, im using method getUserPrincipal() in order to get the user in the actual session, it doesn't work at the first page load (returns null) but when i reload the page it works perfectly.
Im using
spring framework 5.3.9
javaee-api 8.0
weblogic 14.1.1
vuejs 3.2.37
Here is my java controller
#GetMapping("/username")
public ResponseEntity<?> getname(HttpServletRequest request) {
Map<String, String> response = new HashMap<String, String>();
String username = "";
try {
Principal p = request.getUserPrincipal();
if (p == null || p.getName() == null || p.getName().equals("")) {
username = "undefined";
} else {
username = p.getName();
}
response.put("username", username);
return new ResponseEntity<Object>(
response, HttpStatus.OK);
} catch (Exception ex) {
System.out.println(ex.getMessage());
response.put("username", "");
return new ResponseEntity<Object>(response, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
I have tried something a little tricky, reload the page in my Vue file when username is undefined, it works but i need this to work at first load.
Can you help me with this issue?
Thanks
Is it possible the first request is sent without user authentication?
That's what browsers would typically do, and only when the server responds with HTTP status 403 they would try again with credentials.
To not deal with such fuss in your application you could simply set a security constraint that only allows authenticated users to access. The container will then automatically send the 403 response and your application would only see valid traffic.
Related
I want to be able to fetch a param from the redirect url whenever it is automated. I am having difficulties doing this as I am getting a bad request after I created another endpoint to effect this.
I have an endpoint that works fine. The endpoint is a get method. Loading the endpoint takes a user to a page where they need to provide some necessary details. Once these details have been verified, the user is redirected to my redirecr_uri. The redirect_uri now contains important information like session_id, code, etc. The most important thing I need is the code. I need to pass the code into yet another endpoint which will return an access token.
I have manually done this process and it works but I want it to be done automatically because I can't keep doing that when I push the code to staging or production.
Here is the endpoint that redirects as well as the method.
#GetMapping("/get-token")
public RedirectView getBvn() throws UnirestException {
return nibss.getAccessToken();
}
This is the method that the controller calls
public RedirectView getAccessToken() throws UnirestException {
String url = "https://idsandbox.nibss-plc.com.ng/oxauth/authorize.htm?scope=profile&acr_values=otp&response" +
"_type=code&redirect_uri=https://www.accionmfb.com/&client_id=0915cd00-67f2-4768-99ac-1b2ff9f1da2e";
RedirectView redirectView = new RedirectView();
redirectView.setUrl(url);
return redirectView;
}
When the user provides the right information they are redirected to something like this
https://www.accionmfb.com/?code=9ad91f13-4698-4030-8a8f-a857e6a9907e&acr_values=otp&scope=profile&session_state=fa525cabc5b62854c73315d0322fd830c12a5941b89fd8e6e518da369e386572.b78a3d21-e98e-4e9a-8d60-afca779d9fad&sid=fd60ab92-ef37-4a5b-99b9-f8f52321985d
It is important to state that this 3rd party API I am trying to consume uses oath2.0 client authentication.
I created this endpoint to get the code from the redirected_uri
#GetMapping("/redirect-url")
public void handleRedirect(#RequestParam("code") String code) throws UnirestException {
if(Objects.nonNull(code) || !code.isEmpty()){
nibss.getToken(code);
log.info("Code is not being passed {}", code);
} else {
log.info("Code is not being passed {}", code);
}
}
public String getToken(String code) throws UnirestException {
log.info("This is the code here oooooooooo {}", code);
String url = "https://idsandbox.nibss-plc.com.ng/oxauth/restv1/token";
String parameters = "client_id=0915cd00-67f2-4768-99ac-1b2ff9f1da2e&code="+code+"&redirect_uri=https://www.accionmfb.com/&grant_type=authorization_code";
HttpResponse<String> apiResponse = Unirest.post(url)
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization", "Basic MDkxNWNkMDAtNjdmMi00NzY4LTk5YWMtMWIyZmY5ZjFkYTJlOlRVRnEwcGFBQXRzbzBZOEcxMkl2WFZHUmx6WG5zaERiaGt1dzI1YUM=")
.body(parameters)
.asString();
//JSONObject apiJson = apiResponse.getBody().getObject();
//return apiJson.getString("access_token");
JSONObject json = new JSONObject(apiResponse.getBody());
String accessToken = json.getString("access_token");
log.info(accessToken);
return accessToken;
}
But this is not working, I get 400 whenever I hit the second endpoint. What am I doing wrong?
The redirect_uri that you are passing to the OAuth server is https://www.accionmfb.com which does not include the path /redirect-url so the redirect never hits your method.
Either register and pass a callback uri like redirect_uri=https://www.accionmfb.com/redirect-url
Or change #GetMapping("/redirect-url") to #GetMapping("/")
I am trying to move my Login to application outside of my performance tests made in Gatling.
Login authentication is by stored Cookie, so I want to log in to app and obtain an auth cookie in another Class, and then parse it directly into "addCookie" method in gatling (so i guess it should be stored in session of next gatling performance tests)
I have method getAuthCookie in class CookieSaver
public String getAuthCookie() throws Exception {
Properties prop = new Properties();
prop.load(new FileInputStream("data.properties"));
String user = prop.getProperty("username");
String pass = prop.getProperty("password");
String url = prop.getProperty("url");
Map<Object, Object> data = new HashMap<>();
data.put("username", user);
data.put("password", pass);
HttpRequest request2 = HttpRequest.newBuilder()
.POST(buildFormDataFromMap(data))
.uri(URI.create(url))
.setHeader("User-Agent", "Java 11 HttpClient Bot")
.header("Content-Type", "application/x-www-form-urlencoded")
.build();
HttpResponse<String> response = httpClient.send(request2, HttpResponse.BodyHandlers.ofString());
String cookie = String.valueOf(response.headers().firstValue("set-cookie"));
System.out.println(response.statusCode());
System.out.println(response.body());
String fixedCookie = cookie.replace("Optional", "");
return fixedCookie;
}
and I am trying to use it in .exec(addCookie........
ChainBuilder search;
{
try {
search = exec(flushCookieJar())
.exec(flushHttpCache())
.exec(addCookie(Cookie("set-cookie", authCookie.getAuthCookie()).withDomain("localhost")))
.pause(1)
.exec(
http("Contacts")
.get("/transactions/contacts")
.check(
status().is(200).saveAs("Response")
))
.exec(session -> {
System.out.println("session1: " +session);
return session;
})
.exec(flushSessionCookies())
.pause(1);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
I obtain the Cookie and set it correctly into session (verified in logs after run), but i obtain 401 when trying to run next step of performance tests, which is simple get
I've tried log in to application inside performance tests and it's working correctly, but i would need to move log in outside of performance tests
This works:
ChainBuilder search;
{
try {
search = exec(http("Home").get("/"))
.exec(
http("Login")
.post("/login")
.formParam("username", "user")
.formParam("password", "pw")
.check(
status().is(200)
)
)
.pause(1)
.exec(
http("Contacts")
.get("/transactions/contacts")
.check(
status().is(200).saveAs("Response")
))
.exec(session -> {
System.out.println("sesja1: " +session);
return session;
})
.exec(flushSessionCookies())
.pause(1);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
When you're sending cookies they need to go in Cookie header, Set-Cookie is what server uses for creating the cookies
I don't think you really "moving" your login "out of" your performance tests, you're just using your custom code which will still be present in metrics and impacting response time and throughput (and leaving the connection to the server open by the way), maybe it worth simulating logins somewhere in before hook and use a feeder when the real simulation starts
Having load generator and the system under test running on the same machine is not the best idea as they both will be struggling for the operating system resources and even a comprehensive APM tool won't be able to tell you where the bottleneck is
I am using a method where it calls another REST API to retrieve an ID from the DB. When I run the veracode scan for the class I am getting Security flaw "Server-side Request Forgery" at below line.
response = resttemplate.getForEntity(resturl, String.class);
Not sure How to fix this issue. Any help is appreciated. Below is my full code for that method.
public static String getIDFromDB(String resturl) {
String id = null;
RestTemplate resttemplate = new RestTemplate();
ResponseEntity<String> response = new ResponseEntity<>(HTTPStatus.OK)
try {
response = resttemplate.getForEntity(resturl, String.class);
if (response.getStatusCode == HTTPStatus.OK && response.getBody.trim() != null) {
id = response.getBody.trim() ;
}
} Catch(Exception e) {
log.error("failed to get msgID: {}", e);
}
}
This is because you are allowing in your code to pass the resturl completely in your code, so it enables the attacker to bypass and route the URL to their intended destination.
To avoid this, so should externalise and refer the URL having domain and the application contexts with operation name in config files or dB
I am trying to implement a simple SSO feature in my spring-based web application. Scenario:
I have a main application Application1 and a secondary Application2. Both have their own login mechanisms (using spring-security login-forms) in place.
I want to implement SSO in Application1, so that when user logs-in to Application1, he can also seamlessly access Application2 via a link without having to fill up login details for Application2.
Here's what I have tried:
I created an API in Application2 which takes email as input, validates it, creates user session, and returns a url string.
#RequestMapping(path = "/sso/login", consumes = "application/json", method = RequestMethod.POST)
public String login(#RequestBody SSOparams params, HttpServletRequest req, ModelMap model) {
// 1. validates email from params
// 2. creates Authentication object:
UsernamePasswordAuthenticationToken authReq = new UsernamePasswordAuthenticationToken(username, password);
Authentication auth = authManager.authenticate(authReq);
SecurityContext sc = SecurityContextHolder.getContext();
sc.setAuthentication(auth);
HttpSession session = req.getSession(true);
session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, sc);
// 3. returns a url string:
return "/user/dashboard";
}
User logs-in to Application1.
Inside Application1's home-page, when user clicks on a Application2's link, a call is made to Application1's controller method.
Application1's controller method calls Application2's login API with an email parameter, and finally redirects to the url returned from the API.
Application1's controller method:
#RequestMapping(value = "/callapplication2", method = RequestMethod.POST)
public String callapplication2(ModelMap model,HttpSession session) {
String output = "";
String redirectionUrl = "";
try {
// 1. calling application2's login API
URL url = new URL("http://localhost:8080/application2/api/sso/login");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
String input = "{\"uniqueemail\":\"abc#gmail.com\"}";
OutputStream os = conn.getOutputStream();
os.write(input.getBytes());
os.flush();
BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream())));
while ((output = br.readLine()) != null) {
redirectionUrl = redirectionUrl + output;
}
conn.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// 2. returns a url
return "redirect:http://localhost:8080/application2" + redirectionUrl ;
}
All of the above is working fine. But when I login to Application1 and click on application2's link, I expect application should redirect to
http://localhost:8080/application2/user/dashboard
without having to fill up credentials.
But instead, Application2's login page opens. I can see in the network console of Chrome that /user/dashboard is being called, but since the page is secured, I am redirected to application2's login page.
Does this mean that the authentication I created using API is not being used. What am I missing?
Best approach is to set filter in web.xml and put it in top of the list.
Whenever your application will get any request it will first go to the filter and there you will check that session is present or not if its null then simply redirect to your sso login page else respective landing page.
Now in your case,
Solution i can see
1) Put filter into app2 web.xml
2) Now when you redirect from app1 to app2 (Pass one parameter anything like username, email whatever)
3) Store it into the session.
4) Whenever any request will come to app2 you will first verify session from filter, If username found that means user not need to login again else redirect to sso login page.
Thats standars steps (I belive)
5) Having a peek into your implementation.Specifically you have to add one more step into app filter. When you are redirecting from app1 for te first time with http://localhost:8080/application2/user/dashboard ( You will pass one parameter along with this url as explained above).
This let you to check 2 condition into your filter. Either request should have valid parameter or username should be into session. If any condition stated true you can let redirect request to further else you have to redirect to login page.
Hope this will help to resolve your issue.
I am following this post: Outlook RestGettingStarted. From my Java app I am trying to get AccessToken and RefreshToken. When I made Authorization code request, it ended into following error:
Sorry, but we’re having trouble signing you in. We received a bad
request.
Additional technical information: Correlation ID:
ed838d66-5f2e-4cfb-9223-a29082ecb26f Timestamp: 2015-08-20 10:20:09Z
AADSTS90011: The 'resource' request parameter is not supported.
NOTE: URL formation is correct as per documentation.
So, I removed "resource" query parameter from my code. And redirected authorize url in browser. On user consent I got authorization code. Using this code I got AccessToken. But when I try to connect with Outlook IMAP server it failed. Java ref Link for details: Java OAuth2
But it gives me error:
[AUTHENTICATIONFAILED] OAuth authentication failed.
NOTE: I added correct scope, and user email.
Then using obtained Access Token I made Mail Rest API call to get Messages from User Inbox. It ended into following error:
HTTP response:
{"error":{"code":"MailboxNotEnabledForRESTAPI","message":"REST API is
not yet supported for this mailbox."}}
Can anyone help me for following:
What is the exact cause for: "AADSTS90011: The 'resource' request parameter is not supported" after following Outlook dev docs.
How to resolve "MailboxNotEnabledForRESTAPI" error.
Is it possible to connect using java mail APIs to Outlook IMAP server with correct AccessToken ?
I ran into this recently, but don't remember which solved it. One main issue is in the documentation in that it is varying. It will tell you to attach "resource", but that is for something else like Azure.
Here is the code I used:
First request to send:
private static final String USER_OAUTH2_AUTHORIZE_URL = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize";
public String getOAuthDialog(Http.Request request) {
return USER_OAUTH2_AUTHORIZE_URL
+ "?client_id=" + config.getClientId()
+ "&redirect_uri=" + getOutlookLoginRedirect(request)
+ "&response_type=code"
+ "&scope=https%3A%2F%2Foutlook.office.com%2Fmail.send%20" +
"https%3A%2F%2Foutlook.office.com%2Fmail.readwrite%20" +
"offline_access%20openid%20email%20profile"
+ "&state=" + crypto.generateSignedToken();
}
Scope was the hardest thing to figure out. I found a lot of ones that did not work. And it wasn't clear that I needed to separate them with spaces.
Then they will send you a request to your redirect url that was supplied. It will contain a code which you need to exchange for the data you requested in the scope. The redirect url that is supplied needs to be the exact same. Also you need to register the redirect url on your application portal under the Platform->Add Platform->Redirect URI->Add Url
private static final String USER_ACCESS_TOKEN_URL = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
private Map<String, String> sendOutlookUserOAuthRequest(Http.Request request, String code) {
WSClient ws = WS.client();
HttpParameters params = new HttpParameters();
params.put("client_id", config.getClientId(), true);
params.put("client_secret", config.getClientSecret(), true);
params.put("code", code, true);
params.put("redirect_uri", getOutlookLoginRedirect(request), true);
params.put("grant_type", "authorization_code");
String postParams = OAuthUtil.parametersToString(params);
WSRequest wsRequest = ws.url(USER_ACCESS_TOKEN_URL)
.setMethod("POST")
.setContentType("application/x-www-form-urlencoded")
.setBody(postParams);
WSResponse wsResponse = wsRequest.execute().get(10, TimeUnit.SECONDS);
Map<String, String> result = new HashMap<>();
if (wsResponse.getStatus() != HttpStatus.SC_OK) {
return result;
}
JsonNode node = wsResponse.asJson();
if (node.hasNonNull("access_token")) {
result.put("access_token", node.get("access_token").asText());
}
if (node.hasNonNull("refresh_token")) {
result.put("refresh_token", node.get("refresh_token").asText());
}
if (node.hasNonNull("id_token")) {
String[] tokenSplit = node.get("id_token").asText().split("\\.");
if (tokenSplit.length >= 2) {
try {
JSONObject jsonObject = new JSONObject(new String(Base64.getDecoder().decode(tokenSplit[1])));
if (jsonObject.has("name")) {
result.put("name", jsonObject.get("name").toString());
}
if (jsonObject.has("email")) {
result.put("outlookUid", jsonObject.get("email").toString());
} else if (jsonObject.has("preferred_username")) {
result.put("outlookUid", jsonObject.get("preferred_username").toString());
}
} catch (JSONException e) {
log.error("Error extracting outlookUid from id_token: ", e);
}
}
}
return result;
}
Another request that you might need is to update the refresh token:
private String getAccessTokenFromRefreshToken(User user) {
WSClient ws = WS.client();
HttpParameters params = new HttpParameters();
params.put("client_id", config.getClientId(), true);
params.put("client_secret", config.getClientSecret(), true);
params.put("grant_type", "refresh_token");
params.put("refresh_token", user.getOutlookRefreshToken());
String postParams = OAuthUtil.parametersToString(params);
WSRequest wsRequest = ws.url(USER_ACCESS_TOKEN_URL)
.setMethod("POST")
.setContentType("application/x-www-form-urlencoded")
.setBody(postParams);
WSResponse wsResponse = wsRequest.execute().get(10, TimeUnit.SECONDS);
if (wsResponse.getStatus() != HttpStatus.SC_OK) {
log.error("Failure to refresh outlook access token for user: " + user +
". Received status: " + wsResponse.getStatus() + " : " + wsResponse.getStatusText());
return null;
}
JsonNode node = wsResponse.asJson();
if (node.hasNonNull("access_token")) {
String accessToken = node.get("access_token").asText();
return accessToken;
} else {
log.error("Outlook refresh token failure, 'access_token' not present in response body: " + wsResponse.getBody());
return null;
}
}
One issue I ran into that took far longer than I would have hoped was in getting the clientId and clientSecret. This was because the language microsoft uses wasn't the most explicit. Client Id and application id are used interchangeably. The client secret is also the password that you create on the Application Portal, not to be confused with the Private Key that you can generate.
So you actually want the application_id and the password, although they refer to them as client_id and client_secret with no direct indication as to the lines drawn.
This is all assuming you have set up an application on the Outlook Application Portal. https://apps.dev.microsoft.com/
I hope this helps, although I assume you probably already solved this.
I faced the same problem with Java mail. You need to add service principals for your application on the Azure AD.
Find complete steps explained in Medium article Complete guide: Java Mail IMAP OAuth2.0 Connect Outlook | by Ritik Sharma | Dec, 2022.