asp.net core site behind proxy: Azure B2C signin results in correlation failed - asp.net-core-2.0

I've got a web app that uses Azure B2C sign in. When I run the site locally, with config set to signin to the Azure B2C tenant, everything works as expected.
I then deploy the site to Azure where I have two webapp siting behind a FrontDoor proxy/load balancing configuration. I have also tried the same using traffic manager.
When I click on the Sign In link, which should redirect me to the Sign In B2C page, instead, I am redirected back to my site and get an error on the webpage: Correlation failed.
Assuming I have two web apps in the frontdoor configuration, called:
mywebapp-eastus
mywebapp-westus
and assuming the public domainname is https://www.mywebapp.com
when i make sign in require, in the response headers of the signin-oidc request, I am seeing this:
set-cookie: ARRAffinity=335ad67894a0a02a521f095924a8d7be4f7829a49d21743b7dd9ec8ce66879d7;Path=/;HttpOnly;Domain=mywebapp-eastus.azurewebsites.net
where mywebapp-eastus is actually an individual web app name. I would have expected to see the public domain here, not the individual web app that I connected to.
As can be seen from this Chrome dev tool screenshot, the signin-oidc then results in an error after the redirect has occurred:
I would expect to see this instead:
ARRAffinity=335ad67894a0a02a521f095924a8d7be4f7829a49d21743b7dd9ec8ce66879d7;Path=/;HttpOnly;Domain=www.mywebapp.com
I don't know if this is the underlying reason for the error.
Here is the code that sets up authentication and cookies:
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.Configure<ApiBehaviorOptions>(options =>
{
options.InvalidModelStateResponseFactory = ctx => new ValidationProblemDetailsResult();
});
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddAzureAdB2C(options => Configuration.Bind(AppSettings.AzureB2CSettings, options))
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
options.SlidingExpiration = true;
options.Cookie.HttpOnly = false;
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
});
Update
I have added x-forwarded-proto options with no noticeable effect:
var forwardingOptions = new ForwardedHeadersOptions()
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
};
forwardingOptions.KnownNetworks.Clear();
forwardingOptions.KnownProxies.Clear();
app.UseForwardedHeaders(forwardingOptions);
I thought i had found a related issue here: x-forwarded-proto-not-working
Any idea how I can resolve this issue? What options can I set so that the public domain is correctly populated into the response headers.
The web app is running asp.netcore 2.2 on linux (web app for containers).
Update 2:
I've added logging to the site. It shows that there's an underlying error: Cookie not found and Correlation failed
Information {Protocol="HTTP/1.1", Method="POST", ContentType="application/x-www-form-urlencoded", ContentLength=722, Scheme="http", Host="webapp-eastus.azurewebsites.net", PathBase="", Path="/signin-oidc", QueryString="", EventId={Id=1}, SourceContext="Microsoft.AspNetCore.Hosting.Internal.WebHost", RequestId="0HLKC8D934664:00000001", RequestPath="/signin-oidc", CorrelationId=null, ConnectionId="0HLKC8D934664"} "Request starting HTTP/1.1 POST http://webapp-eastus.azurewebsites.net/signin-oidc application/x-www-form-urlencoded 722"
Debug {EventId={Id=1}, SourceContext="Microsoft.AspNetCore.HttpsPolicy.HstsMiddleware", RequestId="0HLKC8D934664:00000001", RequestPath="/signin-oidc", CorrelationId=null, ConnectionId="0HLKC8D934664"} The request is insecure. Skipping HSTS header.
Debug {EventId={Id=1}, SourceContext="Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware", RequestId="0HLKC8D934664:00000001", RequestPath="/signin-oidc", CorrelationId=null, ConnectionId="0HLKC8D934664"} "POST" requests are not supported
Debug {EventId={Id=25, Name="RequestBodyStart"}, SourceContext="Microsoft.AspNetCore.Server.Kestrel", RequestId="0HLKC8D934664:00000001", RequestPath="/signin-oidc", CorrelationId=null} Connection id ""0HLKC8D934664"", Request id ""0HLKC8D934664:00000001"": started reading request body.
Debug {EventId={Id=26, Name="RequestBodyDone"}, SourceContext="Microsoft.AspNetCore.Server.Kestrel", RequestId="0HLKC8D934664:00000001", RequestPath="/signin-oidc", CorrelationId=null} Connection id ""0HLKC8D934664"", Request id ""0HLKC8D934664:00000001"": done reading request body.
[40m[1m[33mwarn[39m[22m[49m: Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler[15]
'.AspNetCore.Correlation.OpenIdConnect.2KB8HPHJV3KhB2HCDp3C3b5iPXjcdAQLOrz5-6nGnwY' cookie not found.
Warning {EventId={Id=15}, SourceContext="Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler", RequestId="0HLKC8D934664:00000001", RequestPath="/signin-oidc", CorrelationId=null, ConnectionId="0HLKC8D934664"} '".AspNetCore.Correlation.OpenIdConnect.2KB8HPHJV3KhB2HCDp3C3b5iPXjcdAQLOrz5-6nGnwY"' cookie not found.
Information {EventId={Id=4}, SourceContext="Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler", RequestId="0HLKC8D934664:00000001", RequestPath="/signin-oidc", CorrelationId=null, ConnectionId="0HLKC8D934664"} Error from RemoteAuthentication: "Correlation failed.".
Debug {SourceContext="AppBuilderExtensions", RequestId="0HLKC8D934663:00000001", RequestPath="/", CorrelationId=null, ConnectionId="0HLKC8D934663"} Header: "User-Agent": ["Edge Health Probe"]
Debug {SourceContext="AppBuilderExtensions", RequestId="0HLKC8D934663:00000001", RequestPath="/", CorrelationId=null, ConnectionId="0HLKC8D934663"} Header: "X-Client-IP": ["1.1.1.1"]
Information {ElapsedMilliseconds=12.3269, StatusCode=302, ContentType=null, EventId={Id=2}, SourceContext="Microsoft.AspNetCore.Hosting.Internal.WebHost", RequestId="0HLKC8D934664:00000001", RequestPath="/signin-oidc", CorrelationId=null, ConnectionId="0HLKC8D934664"} "Request finished in 12.3269ms 302 "
Debug {EventId={Id=6, Name="ConnectionReadFin"}, SourceContext="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets"} Connection id ""0HLKC8D934664"" received FIN.
Debug {EventId={Id=7, Name="ConnectionWriteFin"}, SourceContext="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets"} Connection id ""0HLKC8D934664"" sending FIN because: ""The client closed the connection.""
Debug {SourceContext="AppBuilderExtensions", RequestId="0HLKC8D934663:00000001", RequestPath="/", CorrelationId=null, ConnectionId="0HLKC8D934663"} Header: "X-Client-Port": ["63761"]
Debug {SourceContext="AppBuilderExtensions", RequestId="0HLKC8D934663:00000001", RequestPath="/", CorrelationId=null, ConnectionId="0HLKC8D934663"} Header: "X-FD-HealthProbe": ["1"]
Debug {SourceContext="AppBuilderExtensions", RequestId="0HLKC8D934663:00000001", RequestPath="/", CorrelationId=null, ConnectionId="0HLKC8D934663"} Header: "X-WAWS-Unencoded-URL": ["/"]
Debug {SourceContext="AppBuilderExtensions", RequestId="0HLKC8D934663:00000001", RequestPath="/", CorrelationId=null, ConnectionId="0HLKC8D934663"} Header: "CLIENT-IP": ["1.1.1.1:63761"]
Debug {SourceContext="AppBuilderExtensions", RequestId="0HLKC8D934663:00000001", RequestPath="/", CorrelationId=null, ConnectionId="0HLKC8D934663"} Header: "X-ARR-LOG-ID": ["5022cb4d-7c63-4a78-ad11-c8474246281d"]
Debug {SourceContext="AppBuilderExtensions", RequestId="0HLKC8D934663:00000001", RequestPath="/", CorrelationId=null, ConnectionId="0HLKC8D934663"} Header: "DISGUISED-HOST": ["webapp-eastus.azurewebsites.net"]
Debug {SourceContext="AppBuilderExtensions", RequestId="0HLKC8D934663:00000001", RequestPath="/", CorrelationId=null, ConnectionId="0HLKC8D934663"} Header: "X-SITE-DEPLOYMENT-ID": ["webapp-eastus"]
Debug {SourceContext="AppBuilderExtensions", RequestId="0HLKC8D934663:00000001", RequestPath="/", CorrelationId=null, ConnectionId="0HLKC8D934663"} Header: "WAS-DEFAULT-HOSTNAME": ["webapp-eastus.azurewebsites.net"]
Debug {SourceContext="AppBuilderExtensions", RequestId="0HLKC8D934663:00000001", RequestPath="/", CorrelationId=null, ConnectionId="0HLKC8D934663"} Header: "X-Original-URL": ["/"]
Debug {EventId={Id=10, Name="ConnectionDisconnect"}, SourceContext="Microsoft.AspNetCore.Server.Kestrel"} Connection id ""0HLKC8D934664"" disconnecting.
Debug {EventId={Id=2, Name="ConnectionStop"}, SourceContext="Microsoft.AspNetCore.Server.Kestrel"} Connection id ""0HLKC8D934664"" stopped.
Just to be clear, I sticky sessions have been activated on the FrontDoor configuration. I also tried stopping one of the web apps during login to see what the effect was. Same behaviour. So, I don't think this is a DataProtection related issue.
Update 3
I think I may have found the underlying issue. I found this error:
AADB2C90006: The redirect URI 'https://webapp-eastus.azurewebsites.net/signin-oidc' provided in the request is not registered for the client id 'zzzzzzz-2121-4158-b0f8-9d164c95000'.
Correlation ID: xxxxxxxxx-xxxxxxxxx-xxxxxxx-xxxxxxx
When I looked for the X-Forwarded * headers in the request, I found they are missing. This is even though I have a block of code that looks like this:
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
in ConfigureServices, in Startup.cs
In Configure() I have this:
public void Configure(IApplicationBuilder app)
{
app.UseForwardedHeaders();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.Use((context, next) =>
{
context.Request.Scheme = "https";
return next();
});
app.UseSession();
app.UseAuthentication();
app.LogAuthenticationRequests(_loggerFactory);
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
The question now is, why would the X-Forwarded-* headers be missing from requests?

Related

DocuSign, Node SDK, JWT auth, Envelope fetch, "The custom error module does not recognize this error."

[FINAL UPDATE]
Fixed per this thread: https://github.com/docusign/docusign-esign-node-client/issues/295
Apparently related to security additions made to DocuSign's API.
I haven't encountered this issue before today. I gave a demo to a client just last week and the same code was working fine. Today I get the following:
{
"status": 444,
"response": {
"req": {
"method": "GET",
"url": "https://demo.docusign.net/restapi...",
"data": {},
"headers": {
"user-agent": "node-superagent/3.8.2",
"x-docusign-sdk": "Node",
"authorization": "Bearer ABC...",
"content-type": "application/json",
"accept": "application/json"
}
},
"header": {
"content-type": "text/html",
"x-docusign-tracetoken": "2eeb8caa-8865-4898-bef9-d3611bfaa3f7",
"x-docusign-node": "DA2DFE5",
"date": "Fri, 17 Jun 2022 01:02:02 GMT",
"content-length": "54",
"connection": "close",
"strict-transport-security": "max-age=31536000; includeSubDomains"
},
"status": 444,
"text": "The custom error module does not recognize this error."
}
}
Through the node SDK I fetch a token using requestJWTUserToken, and set the apiclient's auth header, then I new-up an EnvelopesAPI instance. When I make a call to getEnvelope() or listStatusChanges(), then I get the error above. None of this code has changed in months, and I'm using the same integration key, account, private key, everything. I've demo'd it all a few times now - no issues.
An interesting observation: the error above gives me a URL and token. The token is valid, and if I make a request to the URL (the envelopes endpoint) via Postman using said token, then the request succeeds. So my calls through the SDK seem to be failing for some reason.
I can't seem to crack this one, and now I can't get around it given a couple demo systems that worked just last week.
I'm using the docusign-esign 5.17 module - upgraded from 5.15 in an attempt to fix the issue. No luck.
Where is this error coming from?
[Update 1]
I'm running my node app that is making requests through the DocuSign Node SDK against a proxy so I can see what the failing request actually look like:
They fail the same way.
HTTP/1.1 444
Content-Type: text/html
X-DocuSign-TraceToken: 338534c6-c8c3-4b01-9b66-35d697cd0053
X-DocuSign-Node: DA1DFE4
Date: Fri, 17 Jun 2022 03:55:07 GMT
Content-Length: 54
Vary: Accept-Encoding
Connection: close
Strict-Transport-Security: max-age=31536000; includeSubDomains
The custom error module does not recognize this error.
I'm using Proxyman to catch the request, and like Chrome or Firefox it will let you copy a request as a cURL command. If I copy the failing request as cURL, then run in at the terminal, it succeeds.
[MacBookPro0020]~/source/docusign/jwt-smoke-test:0 (master)
$ curl 'https://demo.docusign.net/restapi/v2.1/accounts/a0a4c81f-.../envelopes?envelope_ids=e750526f-...&envelope_ids=a38b794b...&envelope_ids=a5d8c586-...' \
-H 'Host: demo.docusign.net' \
-H 'User-Agent: node-superagent/3.8.2' \
-H 'X-DocuSign-SDK: Node' \
-H 'Node-Ver: v14.18.3' \
-H 'Authorization: Bearer ABCD...' \
-H 'Accept: application/json' \
-H 'Connection: close' \
-H 'Content-Type: application/json' \
--proxy http://localhost:9090
{"resultSetSize":"1","startPosition":"0","endPosition":"0","totalSetSize":"1","nextUri":"","previousUri":"","envelopes":[{"status":"created","documentsUri":"/envelopes/d97565c8...purgeState":"unpurged","envelopeIdStamping":"true","autoNavigation":"true","isSignatureProviderEnvelope":"false","allowComments":"true","anySigner":null,"envelopeLocation":"current_site"}]}
I'm using a JWT auth token, so again, I'm getting a valid token. Calls through the SDK consistently fail, but cURL'ing and manually requesting through Postman both succeed.
I'm at a loss.
Additional details: I see this same issue on MacOS and Windows (i.e., node app hosting docusign-esign). I'm using auth code grant to send envelopes and query envelope statuses and that works fine. I've used JWT Grant without issue up until this week (just demo'd automated functionality last week and it worked.) I haven't made any code changes to my DocuSign functionality, nor have my colleagues, at least according to the repo's history.
I don't recall ever encountering the error above before. I'd love to know why cURL'ing the same request succeeds. I'd rather not ditch the SDK and roll my own requests, but it wouldn't be difficult.
[Update 2]
Here's a simple repro - it's a quick and dirty copy of the QuickStart demo project for the node SDK. I'm using only docusign-esign.
Exact. same. issue.
Again, I can take that token and drop it into cURL or postman and the request will succeed. There's not a lot of code here. The token is valid.
async function main() {
// Data used
// dsConfig.dsClientId
// dsConfig.impersonatedUserGuid
// dsConfig.privateKey
// dsConfig.dsOauthServer
let dsConfig = dsConfig_customer; // defined globally
const jwtLifeSec = 10 * 60, // requested lifetime for the JWT is 10 min
dsApi = new docusign.ApiClient();
dsApi.setOAuthBasePath(dsConfig.dsOauthServer.replace('https://', '')); // it should be domain only.
const results = await dsApi.requestJWTUserToken(dsConfig.dsClientId,
dsConfig.impersonatedUserGuid, 'signature impersonation', dsConfig.privateKey,
jwtLifeSec);
console.log( results.body.access_token );
const userInfo = await dsApi.getUserInfo(results.body.access_token);
dsApi.setBasePath(userInfo.accounts[0].baseUri + '/restapi');
dsApi.addDefaultHeader( 'Authorization', 'Bearer ' + results.body.access_token );
const envelopesAPI = new docusign.EnvelopesApi(dsApi);
const res = await envelopesAPI.getEnvelope( dsConfig.accountID, 'e1917111-2900-48e8-9054-799169379c8a', null );
console.log(res);
return {
accessToken: results.body.access_token,
tokenExpirationTimestamp: expiresAt,
userInfo,
account: userInfo.accounts[0]
};
}
main().then(result => console.log(result)).catch(err=>console.error(err));
...
header: {
'content-type': 'text/html',
'x-docusign-tracetoken': '685b6226-a0d3-4547-94c7-df0216d884a3',
'x-docusign-node': 'DA2DFE188',
date: 'Fri, 17 Jun 2022 05:20:12 GMT',
'content-length': '54',
vary: 'Accept-Encoding',
connection: 'close',
'strict-transport-security': 'max-age=31536000; includeSubDomains'
},
statusCode: 444,
status: 444,
statusType: 4,
info: false,
ok: false,
redirect: false,
clientError: true,
serverError: false,
error: Error: cannot GET /restapi/v2.1/accounts/49754554-ABCD-.../envelopes/e1917111-2900-48e8-9054-799169379c8a (444)
...
I finally manage to resolve the issue by downgrading my Docusign SDK NodeJS version from 5.15.0 to 5.7.0 in my package.json file.
An issue relative to this can be find there, I really hope this issue will be resolved anytime soon.
Edit :
The Docusign support actually took action on this (the github's issue from above is now closed), it might be interesting to test again with the latest version of Docusign SDK NodeJS (or the one you were using previously).

xamarin azure push notifications - how to get Json values in xamarin

I am passing http POST request to Azure web API. I am sending push notifications from Azure notification hub. So I am passing below values as json text.
{
"textMessage": "Hello World",
"action": "action_b",
"appName": "Order",
"parameters": ["WO=1123232, mystring=abc, show=false"]
}
Message is sending successfully. But I need to get AppName and parameter values when push notification received. In xamarin forms i tried to get json body value like this.
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(Config.BackendServiceEndpoint);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
I am getting this error
StatusCode: 404, ReasonPhrase: 'Not Found', Version: 1.1, Content: System.Net.Http.HttpConnection+HttpConnectionResponseContent, Headers:
{
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
Set-Cookie: ARRAffinity=eb8046a771af187481bd962f4b2b59093d136d8cb9f93ea86e26e730ef047fb5;Path=/HttpOnly;Secure;Domain=myapi.azurewebsites.net Set-Cookie: ARRAffinitySameSite=eb8016a771af187481bd932f443b59093d134d8cb9f92ea86e26e780ef047fb2;Path=/;HttpOnly;SameSite=None;Secure;Domain=myapi.azurewebsites.net
Date: Wed, 31 Mar 2021 10:32:10 GMT
Content-Length: 0
}}

Logstash HTTP Output Plugin Error Could not fetch URL, Network is unreachable (connect failed)

I have configured a couple of pipelines in logstash, In one of the pipeline I have configured an http out plugin like
output {
http {
url => "url"
http_method => "post"
request_timeout => <timeout>
automatic_retries => <retry count>
retry_failed => false
}
}
For testing purpose, I configured a webhook url in url section and checked that it is receiving GET/POST requests using curl. But when logstash try to post data on the url using logstash.outputs.http plugin, it fails with ::Manticore::SocketException,
Detailed Error:
[HTTP Output Failure] Could not fetch URL {:url=>"url", :method=>:post, :body=> "some-body" :message=>"Network is unreachable (connect failed)", :class=>"Manticore::SocketException", :backtrace=>["/usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/manticore-0.7.0-java/lib/manticore/response.rb:37:in block in initialize'", "/usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/manticore-0.7.0-java/lib/manticore/response.rb:79:in call'", "/usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/logstash-output-http-5.2.4/lib/logstash/outputs/http.rb:239:in send_event'", "/usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/logstash-output-http-5.2.4/lib/logstash/outputs/http.rb:175:in send_events'", "/usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/logstash-output-http-5.2.4/lib/logstash/outputs/http.rb:124:in multi_receive'", "org/logstash/config/ir/compiler/OutputStrategyExt.java:138:in multi_receive'", "org/logstash/config/ir/compiler/AbstractOutputDelegatorExt.java:121:in multi_receive'", "/usr/share/logstash/logstash-core/lib/logstash/java_pipeline.rb:293:in block in start_workers'"], :will_retry=>true}

logoutSuccessUrl not working in Spring Boot

I have a simple Gradle Spring Boot (v 1.3.3) WebMVC application I'm running from the command line via "Gradle bootrun". I am also including Spring Security and am overriding some default security configuration by including a java security config class. My build file is
buildscript {
ext {
springBootVersion = '1.3.3.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'spring-boot'
jar {
baseName = 'readinglist'
version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.springframework.boot:spring-boot-starter-thymeleaf')
compile('org.springframework.boot:spring-boot-starter-web')
compile("org.springframework.boot:spring-boot-starter-security")
runtime('com.h2database:h2')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
eclipse {
classpath {
containers.remove('org.eclipse.jdt.launching.JRE_CONTAINER')
containers 'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8'
}
}
task wrapper(type: Wrapper) {
gradleVersion = '2.9'
}
My security configuration class is
package readinglist;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private ReaderRepository readerRepository;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/readingList").access("hasRole('READER')")
.antMatchers("/**").permitAll()
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error=true")
.and()
.logout()
.logoutSuccessUrl("/"); // Added .and()....logoutSuccessURL()
}
#Override
protected void configure(
AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(new UserDetailsService() {
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
UserDetails userDetails = readerRepository.findOne(username);
if (userDetails != null) {
return userDetails;
}
throw new UsernameNotFoundException("User '" + username + "' not found.");
}
});
}
}
I also have a home controller that maps the URL "/" to the view home.html
When I run the application and go to localhost:8080/ I get the home page.
When I try to access the URL "/readingList", I get the custom login page. If I enter incorrect credentials, I return to the login page for another try. If I enter valid credentials I get the readingList page. So far so good. The problem is with the logoutSuccessURL("/"). When I go to the URL "/logout" this should log me out and take me back to "/", but instead I get the following error showing in the browser:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Thu Mar 03 19:31:24 PST 2016
There was an unexpected error (type=Not Found, status=404).
No message available
I turned on debug for security and when I hit my logout link I get the following:
2016-03-03 19:48:45.033 DEBUG 22401 --- [io-8080-exec-10] o.s.web.servlet.DispatcherServlet : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/error]
2016-03-03 19:48:45.033 DEBUG 22401 --- [io-8080-exec-10] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /error
2016-03-03 19:48:45.036 DEBUG 22401 --- [io-8080-exec-10] s.w.s.m.m.a.RequestMappingHandlerMapping : Returning handler method [public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)]
2016-03-03 19:48:45.036 DEBUG 22401 --- [io-8080-exec-10] o.s.web.servlet.DispatcherServlet : Last-Modified value for [/error] is: -1
2016-03-03 19:48:45.046 DEBUG 22401 --- [io-8080-exec-10] o.s.w.s.v.ContentNegotiatingViewResolver : Requested media types are [text/html, text/html;q=0.8] based on Accept header types and producible media types [text/html])
2016-03-03 19:48:45.047 DEBUG 22401 --- [io-8080-exec-10] o.s.w.s.v.ContentNegotiatingViewResolver : Returning [org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$SpelView#7ae1a0fb] based on requested media type 'text/html'
2016-03-03 19:48:45.047 DEBUG 22401 --- [io-8080-exec-10] o.s.web.servlet.DispatcherServlet : Rendering view [org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$SpelView#7ae1a0fb] in DispatcherServlet with name 'dispatcherServlet'
2016-03-03 19:48:45.055 DEBUG 22401 --- [io-8080-exec-10] o.s.web.servlet.DispatcherServlet : Successfully completed request
I'm not sure why DispatcherServlet is trying to lookup "/error". I am also not being logged out since if I try to go to the URL "/readingList" again, I am not prompted for credentials.
I did a little more testing by loggin in, then going to the URL "/logout" manually. I got the following in my log:
2016-03-04 16:39:31.170 DEBUG 24395 --- [nio-8080-exec-6] o.s.security.web.FilterChainProxy : /logout reached end of additional filter chain; proceeding with original chain
2016-03-04 16:39:31.170 DEBUG 24395 --- [nio-8080-exec-6] o.s.web.servlet.DispatcherServlet : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/logout]
2016-03-04 16:39:31.170 DEBUG 24395 --- [nio-8080-exec-6] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /logout
2016-03-04 16:39:31.171 DEBUG 24395 --- [nio-8080-exec-6] s.w.s.m.m.a.RequestMappingHandlerMapping : Did not find handler method for [/logout]
2016-03-04 16:39:31.171 DEBUG 24395 --- [nio-8080-exec-6] o.s.w.s.handler.SimpleUrlHandlerMapping : Matching patterns for request [/logout] are [/**]
2016-03-04 16:39:31.171 DEBUG 24395 --- [nio-8080-exec-6] o.s.w.s.handler.SimpleUrlHandlerMapping : URI Template variables for request [/logout] are {}
2016-03-04 16:39:31.172 DEBU G 24395 --- [nio-8080-exec-6] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapping [/logout] to HandlerExecutionChain with handler [ResourceHttpRequestHandler [locations=[ServletContext resource [/], class path resource [META-INF/resources/], class path resource [resources/], class path resource [static/], class path resource [public/]], resolvers=[org.springframework.web.servlet.resource.PathResourceResolver#e146f93]]] and 1 interceptor
2016-03-04 16:39:31.172 DEBUG 24395 --- [nio-8080-exec-6] o.s.web.servlet.DispatcherServlet : Last-Modified value for [/logout] is: -1
2016-03-04 16:39:31.172 DEBUG 24395 --- [nio-8080-exec-6] o.s.web.servlet.DispatcherServlet : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
2016-03-04 16:39:31.173 DEBUG 24395 --- [nio-8080-exec-6] o.s.web.servlet.DispatcherServlet : Successfully completed request
2016-03-04 16:39:31.173 DEBUG 24395 --- [nio-8080-exec-6] o.s.s.w.a.ExceptionTranslationFilter : Chain processed normally
2016-03-04 16:39:31.173 DEBUG 24395 --- [nio-8080-exec-6] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2016-03-04 16:39:31.196 DEBUG 24395 --- [nio-8080-exec-6] o.s.web.servlet.DispatcherServlet : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/error]
....
It seems like the "/logout" URL is not working which I don't undertand. I though the default logout URL is "/logout".
I found a solution in a similar project here, http://spr.com/part-5-integrating-spring-security-with-spring-boot-web/. In my security config I changed
.logout()
.logoutSuccessUrl("/")
.and()
...
to
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/")
.and()
...
I'm not sure if this is the "preferred" solution, and I don't know why the call to logoutRequestMatcher(...) is needed. I have other (non-Spring-Boot) Spring Security projects that don't use the call to logoutRequestMatcher(...), and the .logout(...).logoutSuccessUrl(...) call works just fine.
.and()
.logout()
.invalidateHttpSession(true)
.logoutUrl("/logout")
.permitAll();
i prefer this solution because it wipe the session data so you can't access anymore try it yousrself to make sure open the developer tools in the browser and see the session !!

"The access grant authorization_code is not supported" from Azure AD using Oauth 2

I am in the middle of an Authorization Code Grant Flow with Azure AD. Even though the documentation says the grant_type should be authorization_code, I am getting an error message about this property.
POST https://login.windows.net/SOME_AZURE_AD_UUID/oauth2/token?api-version=1.0
Content-Type: application/x-www-form-urlencoded
client_id=SECRET_CLIENT_ID
&client_secret=SECRET_CLIENT_SECRET
&code=SECRET_CODE
&grant_type=authorization_code
&redirect_uri=https://myserver.example.com/login/auth_return
&resource=https://myserver.example.com/
&scope=openid email
(edit: whitespace added for clarity)
The error I am getting back:
HTTP/1.1 400 Bad request
Content-Length: 436
X-Content-Type-Options: nosniff
X-Powered-By: ASP.NET
Request-Id: SOME_REQUEST_ID
X-Ms-Request-Id: SOME_REQUEST_ID
Strict-Transport-Security: max-age=31536000; includeSubDomains
Set-Cookie: x-ms-gateway-slice=slicea; path=/; secure; HttpOnly, stsservicecookie=acs; path=/; secure; HttpOnly
Server: Microsoft-IIS/8.0
Cache-Control: private
Date: Wed, 20 Aug 2014 14:44:08 GMT
Content-Type: application/json; charset=utf-8
{
"correlation_id": "SOME_CORRELATION_ID",
"error": "unsupported_grant_type",
"error_codes": [
70003
],
"error_description": "
ACS70003: The access grant 'authorization_code' is not supported.\r\n
Trace ID: SOME_TRACE_UUID\r\n
Correlation ID: SOME_CORRELATION_ID\r\n
Timestamp: 2014-08-20 14:44:08Z",
"timestamp": "2014-08-20 14:44:08Z",
"trace_id": "SOME_TRACE_UUID"
}
(whitespace added for clarity)
This request does work if I change grant_type to client_credentials (but I have not found a way to use the resulting token for what I need). It also works if I change some URLs to point to Google instead of Azure AD.
Is there a mistake with these requests or does the service genuinely not support the documented grant_type of authorization_code?
This is a bug I believe, and it took me 2-3 days to figure it out. Please do the following to get it working,
1) Remove the "?api-version=1.0" from your URL. I know it sounds strange but trust me their documentation is a mess.
2) Add a "Content-Type": "application/x-www-form-urlencoded" header in your request (hence you'll have to encode the post data values ... for example redirect_url=(encodedURL) etc
3) Remove unnecessary fields from post data REFER ... it should be like
{
'grant_type': "authorization_code",
'resource': "your resource",
'client_id': "your client Id",
'redirect_uri': "your redirect URL",
'client_secret': "your client secret",
'code': "the code u got"
}
I see you have done point 2 so you'll need to do point 1 and you're good to go.
Furthermore, if you want to get access_token quickly(if nothing I said works for you), then pass "client_credentials" in grant_type and you'll get a smaller response with access_token. But if you want the complete response with refresh_token as well, you'll have to do all those steps.
EDIT:
There is one more mistake in their documentation, for Refresh Tokens >>> the URL should be oauth2/token and NOT oauth2/authorize
Hope this helps!
try this
'grant_type':"client_credentials",
'resource': "your resource",
'client_id': "your client Id",
'redirect_uri': "your redirect URL",
'client_secret': "your client secret",

Resources