I am using spring security with the below configurations.
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.securityContextRepository(securityContextRepository)
.authorizeExchange()
.anyExchange().authenticated()
.and()
.formLogin().disable()
.httpBasic().disable()
.csrf().disable()
.logout().disable();
return http.build();
}
And my ShipxpressReactiveSecurityContextRepository load method looks like below.
#Override
public Mono<SecurityContext> load(ServerWebExchange serverWebExchange) {
String authorization = CollectionToolkit.getFirstElement(
serverWebExchange.getRequest().getHeaders().get(ShipxSecurityConstant.Header.AUTHORIZATION_HEADER));
if (StringToolkit.isNotEmpty(authorization)) {
return authenticate(authorization, serverWebExchange);
} else {
return Mono.empty();
}
}
My use case is properly working. but my issue is here when I try to access my API from the browser (ex : localhost:8180/dmu) Spring browser prompt to authentication. in that situation if i enter the wrong user / password i cant change it with next request.
Because There is a "Authorization" request header with invalid authentication.
I have two questions.
How we can remove default request headers from the browser ( access get methods from browser URL)
how i can disable to generate default request headers from spring.
Example screenshot:
Related
I am creating a login server using Identityserver4. I am using ASP.net core 3.1 for functionality, and angular 9 project for serving static files for login/registeration screens. The angular project is being served from within the .netcore project's wwwroot folder.
My flow goes like this
javascript client calls OIDC user manager's signInRedirect() method with following configurations
This sends a call to my Login method to render the angular's login component. Once the user fills in credentials, a second call is sent to my Login method return this.http.post('Account/Login', {UserName, Password, ReturnUrl}, {headers, responseType:'text'});
On successfull login, I do a return Redirect(model.returnUrl);
[HttpGet]
public IActionResult Login(string returnUrl)
{
return Redirect("/login?ReturnUrl=" + WebUtility.UrlEncode(returnUrl));
}
[HttpPost]
public IActionResult Login([FromBody]LoginViewModel model)
{
if (ModelState.IsValid)
{
var user = _userManager.FindByNameAsync(model.UserName).Result;
var result = _signInManager.PasswordSignInAsync(model.UserName, model.Password, false, false).Result;
if(result.Succeeded)
{
return Redirect(model.ReturnUrl);
}
return Ok(new Error { ErrorMessage = "Invalid credentials" });
}
return Ok(new Error { ErrorMessage = "Some error" });
}
In my network tab, I can see that the return Url which is a call to IdentityServer's authorization endpoint /connect/authorize/callbackis successfull.
It has also made a second call to the actual javascript client in point 1 with the authentication successfull.
However, the problem arises here. This request is returning the HTML as string of the JS clients callback.html instead of actually redirecting to that URL(http://localhost:5003/callback.html)
I don't even have any way to access the URL of the returned HTML, otherwise I would have done a window.location.href. How do I capture the URL of the callback page in angular and redirect to it ?
I would need a few more details to remedy this particular situation. However, I did want to offer my expertise in the form of explaining how this is supposed to work. I have an NPM library imng-auth0-oidc that does this very thing, except that it uses NGRX.
Your callback.html should be a static (non-Angular) HTML page. You can find a copy here callback.html. The purpose of this page is to receive the OAUTH2 response and store the token in localStorage, then redirect the response to your Angular application. Once the application is loaded, you'll now have access to your token that is waiting in localStorage.
-Isaac
I tried to code a 'Hello world' type of exercise to learn Spring Boot support for OAuth2, and the minimum required configuration.
Components:
Authorization server
webapp, which will call the auth server to authenticate the user, and will greet him
Expected flow:
I open the webapp
I get redirected to auth server
I login to auth server
I approve the requested scope
I get redirected back to webapp
I see a greeting (webapp should fetch my username from auth server)
The last point fails with 401:Could not obtain access token.
Last redirect link is http://localhost:9001/ui/login?code=wcXMG4&state=JEEYqC
Am I too naive to assume that the below code&configuration should suffice for my expected flow?
Auth server:
#SpringBootApplication
#EnableAuthorizationServer
#EnableResourceServer
#RestController
public class AuthServer {
public static void main(String[] args) {
SpringApplication.run(AuthServer.class);
}
#GetMapping("/whois")
Principal whois(Principal principal) {
return principal;
}
}
Auth server properties:
server.port=9000
server.contextPath=/sso
security.user.name=joe
security.user.password=password
security.oauth2.client.clientId=SOMEAPP
security.oauth2.client.clientSecret=SECRET
security.oauth2.client.authorized-grant-types=authorization_code,refresh_token,password
security.oauth2.client.scope=read
security.oauth2.resource.userInfoUri=http://localhost:9000/sso/whois
Webapp:
#SpringBootApplication
#EnableOAuth2Sso
#RestController
public class UiServer {
public static void main(String[] args) {
SpringApplication.run(UiServer.class);
}
#GetMapping("/")
String helloWorld(Principal principal) {
return "Yay, auth server provided identity, you are " + principal;
}
}
Webapp properties:
server.port=9001
server.contextPath=/ui
security.oauth2.client.client-id=SOMEAPP
security.oauth2.client.client-secret=SECRET
security.oauth2.client.accessTokenUri=http://localhost:9000/sso/oauth/access_token
security.oauth2.client.userAuthorizationUri=http://localhost:9000/sso/oauth/authorize
security.oauth2.resource.user-info-uri=http://localhost:9000/sso/whois
After spinning up with debug on, it turns out that security.oauth2.client.accessTokenUri is incorrect.
The right endpoint is not .../oauth/access_token, but .../oauth/token.
Probably the tutorial I was looking at used outdated uri.
With that fix, this minimum config does what's expected of it, so I'll close the question.
The real fun begins when you try to customize it, bypassing the defaults; to me it seems that spring oauth still has major bugs, and requires hacky/unexpected approach to work around them in few use cases.
I have a simple web-app deployed in Apache Tomcat which has a login page, an upload form and a logout button.
When the login form is submitted I am checking for the credentials and redirecting it to the upload page if the login is successful and if not I am re-directing the request to the login page itself.
I also have a Filter (javax.servlet.Filter) that authenticated whether each request is from a logged in user.
All was working fine yesterday, but come today even with a valid username/password I am redirected to the login page. This only happens in Chrome.
If I use Firefox or open a incognito window in chrome the flow works perfectly fine.
When I debugged, I see that request.session returns null when a redirection is made on a successful login.
My LoginServlet:
if (success) {
........
...........
HttpSession session = request.getSession(true);
session.setAttribute(WebAppConstants.OAUTH_TOKEN_SESSION_ATTRIB, accessToken);
session.setAttribute(WebAppConstants.USER_SESSION_ATTRIB, username);
session.setAttribute(WebAppConstants.IS_LOGGED_IN_SESSION_ATTRIB, true);
session.setMaxInactiveInterval(30 * 60);
Cookie usernameCookie = new Cookie(WebAppConstants.USER_SESSION_ATTRIB, username);
usernameCookie.setMaxAge(30 * 60);
response.addCookie(usernameCookie);
response.sendRedirect(WebAppConstants.UPLOADER_JSP);
} else {
response.sendRedirect(WebAppConstants.INVALID_LOGIN_JSP);
}
My LoginCheckFilter:
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpSession session = request.getSession(false);
String loginURI = request.getContextPath() + "/login.html";
String uri = request.getRequestURI();
this.context.log("Requested Resource::" + uri);
if (session == null && !(uri.endsWith("html") || uri.endsWith("login"))) {
this.context.log("Unauthorized access request");
response.sendRedirect(loginURI);
} else {
filterChain.doFilter(request, response); // Logged-in user found, so just continue request.
}
}
Why is this happening with the Chrome browser?? Have I handled stuff correctly.
Thank You
I tried removing cookies in Chrome and my login chain worked without an issue.
However, I am still trying to get a clear understanding of what actually happened (with chrome) and how did clearing cookies help me.
EDITED:
As per Shadab Faiz's comment above the below answer seems accurate and thus am accepting it:
What happens is that sometimes browser may store previous request data. So when you input wrong credentials, it stored that request. So from next one onwards, whenever you inputted correct info, the previous request with wrong info was sent.
Thanks
I'm beggining to play with Angular2. I have developed a basic RESTful API using Jersey. I tested it and it works fine (with browser and SOAP UI). This is the code:
#GET
#Produces(MediaType.APPLICATION_JSON)
public TwoWordsMessage getMessage() {
TwoWordsMessage message = new TwoWordsMessage();
message.setFirstWord("hello");
message.setSecondWord("world");
return message;
}
I'm tryng to call the service from an Angular2 app:
this.http.request(this.url).subscribe((res: Response) => {
this.message = res.json();
});
I can see (debbuging) that "getMessage" method is called and it returns the TwoWordsMessage object but the Angular2 application never gets it. The same code with the url http://jsonplaceholder.typicode.com/posts/1 works fine.
What I'm doing wrong?
Thanks!
Are you calling the http request inside a component or a service? Does a function or method fire off the http request?
Also, can you see if there are errors coming back from the response? The subscribe method can take three functions as parameters, first one being on success, second on error, third on completion. If there's an error in the AJAX call (400s, 500s, etc), your code would never be able to handle it. Try this:
this.http.request(this.url).subscribe((res: Response) => {
this.message = res.json();
}, (error) => {
console.warn(error)
});
and see what is spit out. To further debug, you can even use the .do() method on the Observable:
this.http.request(this.url)
.do((res: Response) => console.log(res)) // or other stuff
.subscribe((res: Response) => {
this.message = res.json();
});
The .do() method will execute an arbitrary function with the response without actually affecting it.
If not, you could also try changing the http call to http.get(). I don't think that's the problem, but the Angular docs do not state what method is defaulted to with http.request() (although I would be almost certain it's a GET).
I finally got it working. It's a CORS problem.
The console showed the error:
"No 'Access-Control-Allow-Origin' header is present on the requested
resource"
I changed the resource method like this:
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getMessage() {
TwoWordsMessage message = new TwoWordsMessage();
message.setFirstWord("hello");
message.setSecondWord("world");
return Response.status(200).header("Access-Control-Allow-Origin", "*").entity(message).build();
}
You can find useful information here:
http://www.codingpedia.org/ama/how-to-add-cors-support-on-the-server-side-in-java-with-jersey/
I'm working on a node.js server using express and a android native app, using Retrofit 1.9.
For a login API that returns only a true/false answer to the client, should JSON still be used?
As I see it, the server has only to send a status code response:
if(isLegal) {
res.sendStatus(200);
dbConnector.updateUser(token);
}
else{
console.log('Token is not legal');
res.sendStatus(403);
}
But the Retrofit framework tries to convert the response to JSON, which makes me think I must send a JSON object with the answer, though it seems weird.
My retrofit restClient:
public class RestClient {
private static final String URL = SessionDetails.getInstance().serverAddress;
private retrofit.RestAdapter restAdapter;
private ServerAPI serverAPI;
public RestClient() {
restAdapter = new retrofit.RestAdapter.Builder()
.setEndpoint(URL)
.setLogLevel(retrofit.RestAdapter.LogLevel.FULL)
.build();
serverAPI = restAdapter.create(ServerAPI.class);
}
public ServerAPI getService() {
return serverAPI;
}
}
And usage:
restClient.getService().login(token.getToken(), token.getUserId(), new Callback<Void>() {
#Override
public void success(Void aVoid, Response response) {
Log.d("Chooser", "Successful login on server.");
}
#Override
public void failure(RetrofitError error) {
error.printStackTrace();
Log.d("Chooser", "Login failed on server.");
}
});
Using it as it is results with the following error:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING
There are many topics on this issue but no certain answer about the correct (or better) method to use.
Any ideas about the best implementation in these cases?
Sending an empty body with your HTTP response is perfectly legal and some clients may care only about the response status but some clients may expect to get a response so sending a body never hurts and sometimes may be useful.
You can include a JSON response in addition to the HTTP response status:
// Express 4.x:
res.status(403).json({error: 'Token is not legal'});
// Express 3.x:
res.json(403, {error: 'Token is not legal'});
Such an error message can be very useful for the client development. You can get 403 for many reasons, illegal token, expired token, a legal not expired token but for the wrong user that doesn't have some privilege - adding a specific error message in addition to the HTTP response code can tell the client what exactly went wrong and allows the client-side code to show a better error message to the user.
Also, note that true and false are also valid JSON.