AADSTS900561: The endpoint only accepts POST requests. Received a GET request - c#

i am having asp.net web application. i am migrating this application to azure and implementing Azure AD Authentication. AD authentication and Approle based authorization are working as expected. i am facing issue while signing-out from Application.
AAD-Sign-out-response-message
referred msdn-site tried with these settings as well, still i am getting same issue.
need help on resolving this issue!! thanks!!
my signout method in account controller has following
public void SignOut()
{
HttpContext.GetOwinContext()
.Authentication
.SignOut(CookieAuthenticationDefaults.AuthenticationType);
HttpContext.GetOwinContext().Authentication.SignOut(
CookieAuthenticationDefaults.AuthenticationType, OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
Startup.cs configauth as belows
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = redirectUri,
RedirectUri = redirectUri,
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
NameClaimType = "upn",
RoleClaimType = "roles", // The claim in the Jwt token where App roles are provided.
},
Notifications = new OpenIdConnectAuthenticationNotifications()
{
//
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
//
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
AuthenticationFailed = OnAuthenticationFailed
}
});
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
}
web.config with AD configuration details as follows
<add key="ida:ClientId" value="xxxxx-xxxx-xx-xxxx-xxxxxxx"/>
<add key="ida:Tenant" value="xxxxxx.onmicrosoft.com"/>
<add key="ida:AADInstance" value="https://login.microsoftonline.com/{0}"/>
<add key="ida:PostLogoutRedirectUri" value="https://login.microsoftonline.com/common/oauth2/v2.0/logoutsession/"/>
<add key="ida:RedirectUri" value="https://xxxxxxxx.azurewebsites.net/"/>

I tried to re-create your problem, and if you configured the logouturl in the portal like I did, you can try deleting it and then running the program.

Related

Renewing session using Azure AD authentication in ASP.NET MVC project

I have configured Azure AD authentication in an ASP.NET MVC project using OpenId Connect. The authentication works, but the problem is, that after 60 minutes the session is not valid anymore. In my application, it's common that the user is idle for quite some time, and should be able to continue work without having to relogin. This is how I've setup the authentication in Startup.auth.cs:
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions {
CookieManager = new SystemWebCookieManager()
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthorizationCodeReceived = (context) =>
{
return System.Threading.Tasks.Task.FromResult(0);
}
}
});
I have tried to add session refreshing logic as suggested at https://www.cloudidentity.com/blog/2016/07/25/controlling-a-web-apps-session-duration-2/. The problem in that approach is that login window cannot be displayed in a frame.
How can I keep the session valid until user closes browser window? This seems like a common issue that should have some common solution, but I could not find one.
You can add the below highlighted parameter in your code that under ‘new OpenIdConnectAuthenticationOptions’ section before the class: -
app.SetDefaultSignInAsAuthenticationType
(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions {
CookieManager = new SystemWebCookieManager()});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthorizationCodeReceived = (context) =>
{
return System.Threading.Tasks.Task.FromResult(0);
}
UseTokenLifetime = false
}
});'
The authentication session lifetime (e.g., cookies) should match that of the authentication token. The problem is that you need to extend token lifetime in AAD which is set to one hour by default.
Also, please find links to below threads for your reference: -
AzureAD and OpenIdConnect session expiration in ASP.net WebForms
Cookie expiry in ASP NET Core Authentication using Azure AD OpenIdConnect and custom middleware
Thanking you,

Azure AD Open ID Connect OAuth 2.0 in ASP.NET Web APP and Web API Infinite redirect loop

ASP.NET web app to sign in personal accounts and work and school accounts from any Azure Active Directory (Azure AD) instance.
OWIN middleware NuGet packages
Install-Package Microsoft.Owin.Security.OpenIdConnect
Install-Package Microsoft.Owin.Security.Cookies
Install-Package Microsoft.Owin.Host.SystemWeb
OWIN Startup Class
The OWIN middleware uses a startup class that runs when the hosting process initializes. In this quickstart, the startup.cs file located in the root folder. The following code shows the parameter used by this quickstart
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// Sets the ClientId, authority, RedirectUri as obtained from web.config
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
// PostLogoutRedirectUri is the page that users will be redirected to after sign-out. In this case, it is using the home page
PostLogoutRedirectUri = redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
// ResponseType is set to request the id_token - which contains basic information about the signed-in user
ResponseType = OpenIdConnectResponseType.IdToken,
// ValidateIssuer set to false to allow personal and work accounts from any organization to sign in to your application
// To only allow users from a single organizations, set ValidateIssuer to true and 'tenant' setting in web.config to the tenant name
// To allow users from only a list of specific organizations, set ValidateIssuer to true and use ValidIssuers parameter
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false // Simplification (see note below)
},
// OpenIdConnectAuthenticationNotifications configures OWIN to send notification of failed authentications to OnAuthenticationFailed method
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed
}
}
);
}
ASP.NET MVC / Web API
//You can force a user to sign in by requesting an authentication challenge in your controller:
public void SignIn()
{
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties{ RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
ASP.NET Web Form:
protected void Login_click(object sender, EventArgs e)
{
if (!Request.IsAuthenticated)
{
HttpContext.Current.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
The problem has been fixed in ASP.NET core and in the new version of Katana Owin for ASP.NET. To resolve this issue, you can upgrade your application to use ASP.NET Core. If you must continue stay on ASP.NET, perform the following:
Update your application’s Microsoft.Owin.Host.SystemWeb package be at least version 3.1.0.0 and
Modify your code to use one of the new cookie manager classes, for example something like the following:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies",
CookieManager = new Microsoft.Owin.Host.SystemWeb.SystemWebChunkingCookieManager()
});

How to switch Azure AD directory programatically via C# in a multi tenant app

I have ASP.NET MVC app which is configured with a Multi-tenant Azure AD app. So in my Web.config I had to put <add key="ida:Authority" value="https://login.microsoftonline.com/common/" /> so this will work for any one. But now some of my apps users are assigned to different Azure Directories (where Tenant IDs differ). I need to provided a "Switch Directory" functionality like in Azure Portal / Aure IoT Central.
I know if I replace common with the Tenant ID, the app will only work for the particular Tenant.
In the public void ConfigureAuth(IAppBuilder app) of Startup.Auth.cs I have something like below
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions { });
app.UseCookieAuthentication(new CookieAuthenticationOptions ());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
ClientSecret = clientSecret,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
RedirectUri = redirectUri,
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = false,
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = (context) =>
{
string tenantID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
// The above tenantID is always coming from the default directory
string issuer = context.AuthenticationTicket.Identity.FindFirst("iss").Value;
// The above is also always from the default directory (tenant id)
string UPN = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.Name).Value;
return Task.FromResult(0);
},
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
HttpContext.Current.Session["CODE"] = code;
return Task.FromResult(0);
},
AuthenticationFailed = context =>
{
return Task.FromResult(0);
}
}
});
}
So this gives a OWIN context to me which has the details only for the default directory ID. Can anyone help me to change this behavior? so that I can provide a feature to switch between directories.
A hack I made once (with OWIN) was to add a specific query parameter when requesting login that specified a tenant id.
I can't find the source code right now, but an OWIN middleware did the following:
Call next middleware
Is the response a GET, the response statuscode a 302, location header value has the Azure AD host, and the tenant id query parameter is present?
If yes, grab the Location header, switch the value to "https://login.microsoftonline.com/tenant-id/oauth2/authorize?" + locationHeader query string
It's a kind of hack, but works to help a user log in to the right tenant.

Implementing Sliding Expiration in OpenIdConnect with Azure

We have a ASP.Net MVC and have been using OpenIdConnect authentication with Azure AD as the authority. On successful authentication we set the "AuthenticationTicket" Expiry to 8hrs (below i have set to 15 minutes for testing). This 8hrs is fixed, meaning even if user performs activity on the application it won't slide.
But ideally we would want the expiration to slide as a user is active with the system.
Tried setting "SlidingExpiration" to True, even that didn't help. Documentation is not detailed around this topic.
So, How do we implement sliding expiration with OpenIdConnect Authentication?
Below is our code of startup.
namespace TestApp
{
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieManager = new SystemWebCookieManager(),
SlidingExpiration = true,
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
//
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
//
AuthorizationCodeReceived = (context) =>
{
context.AuthenticationTicket.Properties.ExpiresUtc = DateTime.UtcNow.AddHours(8);
context.AuthenticationTicket.Properties.AllowRefresh = true;
context.AuthenticationTicket.Properties.IsPersistent = true;
return Task.FromResult(0);
},
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect("/Home/Error?message=" + context.Exception.Message);
return Task.FromResult(0);
},
}
});
}
}
}
Hopefully someone from the ASP.net or Owin team can jump in with a better way to do this, but below is how I got around the exact same problem.
In order for the cookie authentication to take precedence and return a 401 or redirect when the cookie is expired, you need to set the cookie authentication mode to active and the Open Id authentication mode to passive.
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
SlidingExpiration = true,
ExpireTimeSpan = TimeSpan.FromMinutes(15),
CookieSecure = CookieSecureOption.Always,
LoginPath = Microsoft.Owin.PathString.FromUriComponent("/Logout")
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Passive,
PostLogoutRedirectUri = postLogoutRedirectUri,
}
);
After changing from active to passive, I was getting a 401 response when requesting an authorized resource, but refreshing the page would simply log me back in using the token saved from the OpenID call which is not expired when the token expires. To fix this, I set an action in the logout controller (specified with the LoginPath on the cookie options) to sign the user out of both OpenID and Cookie
[Route("Logout")]
public ActionResult Logout()
{
HttpContext.GetOwinContext().Authentication.SignOut(
OpenIdConnectAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType);
return View("~/Views/Index/Logout.cshtml");
}
Keep in mind, with Open ID set to passive and cookie set to active, all requests will be redirected as unauthorized if they don't contain the cookie (even if they contain a token). Open ID requests will have to specifically call the Open ID handler to be authorized and create the cookie
sources:
https://msdn.microsoft.com/en-us/library/microsoft.owin.security.cookies.cookieauthenticationoptions(v=vs.113).aspx
https://coding.abel.nu/2014/06/understanding-the-owin-external-authentication-pipeline/

IdentityServer3 + AzureAD and RedirectUri Confusion

I'm struggling to understand how IdentityServer3, AzureAD and a Private Database all work together. The biggest problem is how the Redirect URIs are being handled.
My scenario is I have a stand alone IdentityServer3. It's job is to authenticate users against either AzureAD or a private DB. Within the Startup.cs file on the ID3 server, I have the following OpenID Connect code:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map("/identity", s3App =>
{
s3App.UseIdentityServer(new IdentityServerOptions
{
SiteName = "3S",
SigningCertificate = Certificate.Load(),
Factory = new IdentityServerServiceFactory()
.UseInMemoryUsers(InMemoryUsers.Get())
.UseInMemoryClients(InMemoryClients.Get())
.UseInMemoryScopes(InMemoryScopes.Get()),
AuthenticationOptions = new AuthenticationOptions
{
EnablePostSignOutAutoRedirect = true,
EnableSignOutPrompt = false,
IdentityProviders = ConfigureAdditionalIdentityProviders
}
});
});
}
public static void ConfigureAdditionalIdentityProviders(IAppBuilder app, string signInAsType)
{
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "AzureAd",
Caption = "Login",
ClientId = "4613ed32-xxxx-xxxx-xxxx-xxxxxxxxxxxx", // GUID of registered application on Azure
Authority = "https://login.microsoftonline.com/our-tenant-id/",
PostLogoutRedirectUri = "https://localhost:44348/identity",
RedirectUri = "https://localhost:44348/identity",
Scope = "openid email profile",
ResponseType = "id_token",
AuthenticationMode = AuthenticationMode.Passive,
SignInAsAuthenticationType = signInAsType,
TokenValidationParameters = new TokenValidationParameters
{
AuthenticationType = Constants.ExternalAuthenticationType,
ValidateIssuer = false
}
});
}
I don't understand why the ID3 Server would need to have either RedirectUri or PostLogoutRedirectUri...shouldn't that be "passed through" from the application requesting the authentication? After all, we want to get back to the application, not the ID3 Server. Granted, I don't think this is what's causing my problem, but it would be nice to understand why these are here.
I will say, I've gotten "close" to this working.
When my application requiring authentication requests authentication against AzureAD, I'm redirected to the Microsoft Account login screen to enter my username/password for my work account. I submit my credentials and then get redirected back to either the ID3 server or my application, depending on which RedirectUri has been used in the above code.
For the sake of argument, let's say I use my application for the RedirectUri. I will be sent back to the application, but not to the page that initially prompted the authentication challenge, and if I click on a page that requires authentication, I'm sent back to the AzureAD server to log in again, only this time AzureAD recognizes me as already logged in.
Unfortunately, it doesn't appear that the SecurityTokenValidated notification is being acknowledged/set after the redirect from AzureAD.
Here's the code found in the application Startup.cs:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = "https://localhost:44348/identity",
ClientId = "3af8e3ba-5a04-4acc-8c51-1d30f8587ced", // Local ClientID registered as part of the IdentityServer3 InMemoryClients
Scope = "openid profile roles",
RedirectUri = "http://localhost:52702/",
PostLogoutRedirectUri = "http://localhost:52702/",
ResponseType = "id_token",
SignInAsAuthenticationType = "Cookies",
UseTokenLifetime = false,
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = n =>
{
var id = n.AuthenticationTicket.Identity;
var givenName = id.FindFirst(Constants.ClaimTypes.GivenName);
var familyName = id.FindFirst(Constants.ClaimTypes.FamilyName);
var sub = id.FindFirst(Constants.ClaimTypes.Subject);
var roles = id.FindAll(Constants.ClaimTypes.Role);
var nid = new ClaimsIdentity(
id.AuthenticationType,
Constants.ClaimTypes.GivenName,
Constants.ClaimTypes.Role
);
nid.AddClaim(givenName);
nid.AddClaim(familyName);
nid.AddClaim(sub);
nid.AddClaims(roles);
nid.AddClaim(new Claim("application_specific", "Some data goes here. Not sure what, though."));
nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
n.AuthenticationTicket = new AuthenticationTicket(nid, n.AuthenticationTicket.Properties);
return Task.FromResult(0);
},
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType != OpenIdConnectRequestType.LogoutRequest)
return Task.FromResult(0);
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");
if (idTokenHint != null)
{
n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
}
return Task.FromResult(0);
},
AuthenticationFailed = (context) =>
{
context.HandleResponse();
context.Response.Redirect("/Error/message=" + context.Exception.Message);
//Debug.WriteLine("*** AuthenticationFailed");
return Task.FromResult(0);
},
}
});
AntiForgeryConfig.UniqueClaimTypeIdentifier = Constants.ClaimTypes.Subject;
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
}
}
You'll notice that the OpenIdConnectAuthenticationOptions also contain a RedirectUri and a PostLogoutRedirectUri which point to the application, but those don't seem to matter.
Of course, everything works perfectly when I'm logging in using the "cookies" side of things - I see all of my claims for the user. And, spending some time on the phone with Microsoft, they proposed a solution outside of ID3 which worked, but is not the way we need to go. We will have multiple applications authenticating against our ID3 so we need to contain and control the flow internally.
I really need some help trying to figure out this last mile issue. I know I'm close, I've just been staring at this so long that I'm probably staring right at my error and not seeing it.
10/22/2016 Edit
Further testing and enabling Serilog revealed an issue with the RedirectUri and PostLogoutRedirectUri resulted in my adding the /identity to the end of the URIs which corresponds to the value set in app.Map. This resolved the issue of my being returned to the "blank" page of IdentityServer3, I'm now returned to the IdentityServer3 login screen. Azure AD still thinks I'm logged in, I'm just not getting the tokens set properly in my application.
Since the authenticate flow is a little complex, I am trying illustrate it using a figure below:
First the redirect URL need to register to the identity provider, so the server will match the RedirectURL in request to ensure that the response is redirected as expected instead of redirect to like Phishing site( security consideration).
And as the figure demonstrate, to use the Azure AD as the external identity provider for IdentityServer3, we need to register the apps on Azure AD. However since the app is used to communicate with Identity Server, the redirect URL register on the Azure Portal should redirect to the IdentityServer3 instead of URL of app.
For example, the URL of my identity server 3 is https://localhost:44333 then I use code below to add the additional identity providers. And this URL is the redirect URL on the Azure portal:
public static void ConfigureAdditionalIdentityProviders(IAppBuilder app, string signInAsType)
{
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "aad",
Caption = "Azure AD",
SignInAsAuthenticationType = signInAsType,
Authority = "https://login.microsoftonline.com/04e14a2c-0e9b-42f8-8b22-3c4a2f1d8800",
ClientId = "eca61fd9-f491-4f03-a622-90837bbc1711",
RedirectUri = "https://localhost:44333/core/aadcb",
});
}
And the URL of my app is http://localhost:1409/ which is register on the IdentyServer3 and below code is the web app use OWIN OpenId Connect to add the IdentyServer3 as the identity data provider:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "oidc",
SignInAsAuthenticationType = "cookies",
Authority = "https://localhost:44333",
ClientId = "mvc",
RedirectUri = "http://localhost:1409/",
ResponseType = "id_token",
Scope = "openid profile email"
});

Categories