I am trying to implement OAUTH2 for my web application but even though signing in to the application works, refresh tokens result in an HTTP 400 "invalid_grant".
Specifically, the project is an ASP.NET WebAPI with OWIN OAuth provider. This has been killing me for days without luck so any help will be appreciated :)
Have you correctly set OAuthAuthorizationServerOptions.RefreshTokenProvider?
If you need a sample, Katana's sandbox project contains a minimal implementation showing how you can easily configure it to protect and serialize refresh tokens using the data protection block (machine keys on IIS): https://github.com/jchannon/katanaproject/blob/master/tests/Katana.Sandbox.WebServer/Startup.cs#L169-L173
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions {
RefreshTokenProvider = new AuthenticationTokenProvider {
OnCreate = CreateRefreshToken,
OnReceive = ReceiveRefreshToken,
}
});
private void CreateRefreshToken(AuthenticationTokenCreateContext context) {
context.SetToken(context.SerializeTicket());
}
private void ReceiveRefreshToken(AuthenticationTokenReceiveContext context) {
context.DeserializeTicket(context.Token);
}
If it still doesn't work, try enabling tracing to determine the root cause of the invalid_grant error: http://katanaproject.codeplex.com/wikipage?title=Debugging&referringTitle=Documentation
We were getting the same issue when deploying the AuthorizationServer on Azure and trying to access it through localhost.
Later we deployed all 3:
AuthorizationServer
AuthrizationCodeGrant
Resource Server
Made required changes in the Constants\Paths.cs for the deployed URLs.
Even after this it did not work. But once we changed all the paths to HTTPS it all started working smoothly.
Please try that in case you are still stuck.
Related
I want to override Azure AD SSO login for a MVC web application. I don't want to log other applications out in the process, but require login for security purposes. I am using OAuth2.0 with OIDC via Owin for authentication.
I am trying to use the prompt=login query parameter, which should theoretically do the trick. I found a Github reference to this being recently made available in Core but cannot trace how to do it in MVC5.2
Is it possible to do it in the Application Builder? I tried adding .WithExtraQueryParameters ("prompt=login") to the ConfidentialClientApplicationBuilder when getting the access code. No luck.
Is there another workaround if the code doesn't come out-of-the-box?
EDIT: PublicClientApplication allows .WithPrompt(Prompt.Login) while ConfidentialClientApplication does not (also does not allow AcquireTokenInteractive) This is a web app so it needs the confidential builder. Tested using the Public builder and it logs in successfully. But I get an ActiveX instantiation error 'not in a single-threaded apartment'(?) Strange, unless that is how the token is being delivered perhaps. I also tested by changing to multitenant in Azure and by toggling Public client on and off.
Any ideas?
You could use ForceLogin to add paramter to ConfidentialClientApplicationBuilder.
ForceLogin: enables the application developer to have the user prompted for credentials by the service even if this would not be needed. This can be useful if Acquiring a token fails, to let the user re-sign-in. This is done by sending prompt=login to the identity provider. Again, we've seen it used in some security focused applications where the organization governance demands that the user re-logs-in each time they access specific parts of an application.
So, use the code as below:
result = await app.AcquireTokenInteractive(scopes)
.WithPrompt(Prompt.ForceLogin)
.ExecuteAsync();
The Modifier .WithExtraQueryParameters will not help you. You need to use .WithPrompt instead.Please refer article.
Example:
await PublicClientApplication
.AcquireTokenInteractive(scopes, null)
.WithAccount(CurrentUser)
.WithPrompt(Prompt.ForceLogin) // use login for your case
.ExecuteAsync();
I eventually resolved this as follows:
Under Notifications in the ConfigureAuth(IAppBuilder app) add a reference to a new task:
RedirectToIdentityProvider = OnRedirectToIdentityProvider,
Then add the task:
private Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification
<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
// Forces the user to login.
if (notification.ProtocolMessage.Prompt == null)
{
notification.ProtocolMessage.Prompt = "login";
}
return Task.FromResult(0);
}
Hope that helps the next person with this issue.
I am working on a webapi webservice that is proteted by Azure Active Directory. The webservice cumminucates heavily with Office 365 (SharePoint / Yammer) based on the user that is signed in.
To test the web api endpoints I am writing an Console App that let me sign in with my AAD credentials and then calls the endpoints. It works, but looking for something to replace this way of testing the web api. Would be great if it’s more repeatable and that I don’t have to fill in my credentials each time. I was looking for a unit test project but can’t get the Azure AD sign in to work.
Any tips how to make this easier?
The easiest way would be to define the test runner as an application in Azure AD and have it call the API with its own client id and secret.
To do that there are a few things you would need to do:
Add appRoles to your API in its manifest in Azure AD. These are application permissions.
Define your test runner, and have it require the necessary application permissions for your API.
In your test runner you should now be able to get an access token with the client id and secret of the test runner, no user authentication required.
Some setup is needed for app permissions on the API side as well, authorization must also look at the role claims.
You can find an example for defining app permissions and also handling them here: http://www.dushyantgill.com/blog/2014/12/10/roles-based-access-control-in-cloud-applications-using-azure-ad/.
More on defining app permissions: https://stackoverflow.com/a/27852592/1658906.
More info on the application manifest in AAD: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-application-manifest.
EDIT: If you must make calls on behalf of the user in the API, then this of course won't work.
In that case, I would suggest creating a user account with the necessary access for the purpose of running the tests. It would be best not to hard-code its credentials, but store them elsewhere.
If you don't want to "fill in my credentials each time", one workaround is using the Resource Owner Password Credentials Grant flow. This flow is flexible to gain a token easily. In the Console App, you could directly use user account and password to get the access token for your protected web API . The code below is for your reference :
static void Main(string[] args)
{
test().Wait();
}
public static async Task test()
{
using (HttpClient client = new HttpClient())
{
var tokenEndpoint = #"https://login.windows.net/a703965c-e057-4bf6-bf74-1d7d82964996/oauth2/token";
var accept = "application/json";
client.DefaultRequestHeaders.Add("Accept", accept);
string postBody = #"resource=https%3A%2F%2Fgraph.microsoft.com%2F //here could be your own web api
&client_id=<client id>
&grant_type=password
&username=nanyu#xxxxxxx.onmicrosoft.com
&password=<password>
&scope=openid";
using (var response = await client.PostAsync(tokenEndpoint, new StringContent(postBody, Encoding.UTF8, "application/x-www-form-urlencoded")))
{
if (response.IsSuccessStatusCode)
{
var jsonresult = JObject.Parse(await response.Content.ReadAsStringAsync());
var token = (string)jsonresult["access_token"];
}
}
}
}
But the problem is that flow will expose the username and password directly in the code, it brings potential attack risk as well and we will always avoid handling the user credential directly. So make sure you just use this flow for testing in a secure environment. You could refer to this article for more details.
For context, I have OpenIdConnect with an ASP.NET 4 Web App working using Owin (and a lot of help from Modern Authentication with Azure Active Directory for Web Applications.
I now want to secure a separate ASP.NET 5 Web API project (to be hosted in the same AD tenant in Azure as a microservice). I started with the simple ASP.NET 5 WebApi generated in Visual Studio and added the following to the Configure in Startup.cs (at the beginning of the pipeline):
app.UseOAuthAuthentication(new OAuthOptions()
{
ClientId = "71d33a1c-505c-4815-a790-8494dd2bb430",
ClientSecret = "LajQFbf1/Nyt/6zCP5vE5YWj5VC4aNaC3i/SRtEj2sI=",
TokenEndpoint = "https://login.microsoftonline.com/7058f4f0-619f-4c16-ac31-9e209d70ff23/oauth2/token",
AuthorizationEndpoint = "https://login.microsoftonline.com/7058f4f0-619f-4c16-ac31-9e209d70ff23/oauth2/authorize",
AuthenticationScheme = "OAuth2Bearer",
CallbackPath = "/api/values"
});
This gives me an error that indicates SignInScheme must be provided, but I'm not clear on what that value should be. If I add in a string, say "OAuth2Bearer", I get further, but still get a 500 error on the request, but no exception raised in the API app, nor does the breakpoint on the first line in my API controller implementation get hit.
What am I missing? Ideally, I want to then extend the Events of OAuthOptions to add a custom claim, analogous to what I did with OpenIdConnect and the SecurityTokenValidated notification.
The OAuth2 base middleware cannot be used for token validation as it's an OAuth2 client middleware made for handling interactive flows like the authorization code flow. All the existing OAuth2 social providers (e.g Facebook or Google) inherit from this middleware.
You're actually looking for the JWT bearer middleware:
app.UseJwtBearerAuthentication(options => {
options.AutomaticAuthenticate = true;
options.AutomaticChallenge = true;
options.Authority = "[address of your OIDC server]";
options.Audience = "[audience of the access tokens issued by the OIDC server]";
});
To learn more about the different OAuth2/OIDC middleware in ASP.NET Core, don't miss this other SO post: Configure the authorization server endpoint.
I am building a .NET 4.0, ASP.NET MVC 3 intranet application that runs on IIS 7.5 in integrated mode. Windows Authentication is used to govern access to the website. The Windows Authentication module is enabled and all other auth modules are disabled.
Currently when a user provides improper credentials, the Windows Authentication module correctly rejects the credentials and re-displays a login prompt. It does so 3 times, after which a standard .NET 401 Unauthorized Access page is shown. This is expected and desirable.
My goal: I would like to be able to log the details of the failed authentication attempt to my own custom event log. Particularly, to capture the user name that was used in the log in attempt. (I'll accept that capturing the password is not likely to be possible for security reasons.)
Is my goal possible?
I have already built a working an IHttpModule module and added it as an event handler to the WindowsAuthenticationModule, like this:
myWindowsAuthenticationModule.Authenticate += WindowsAuthentication_Authenticate;
But my code does not get called in the case of a failed log in attempt, presumably because WindowsAuthenticationModule has already decided that the log in is failed and so there is no point calling my module. My module does get called after a successful log in attempt, and so I am certain that my event handler is properly set up.
To the best of my knowledge, the WindowsAuthenticationModule does not expose an event that is fired when authentication fails, so that option is out.
Any ideas? Or am I barking up a tree that has no solution?
I was looking at the same issue, and looks like there is no events for windows authentication, even that Authenticate event is common for forms and windows.
But I found a solution to this!
http://www.codeproject.com/Articles/11202/Redirecting-to-custom-401-page-when-quot-Access-de
UPDATE
From original article
protected void Application_EndRequest(Object sender, EventArgs e)
{
HttpContext context = HttpContext.Current;
if (context.Response.Status.Substring(0,3).Equals("401"))
{
if(User.Identity.IsAuthenticated)
{
// this means user is authenticated, but 401 still returned
// which means no access to page or whatever you are trying to access?
}
}
}
UPDATE2
I also found out that this solution doesn't work in all cases. I was testing in different environments, so was working for me, but not for others.
By default IIS will not even run this piece of code and just return it's own error page, what you want to do is tell IIS to let app handle errors.
<httpErrors existingResponse="PassThrough">
</httpErrors>
Now, with that said, IIS won't return any more of custom errors and you will need to handle them in application, ie. not only 401, but 403, 405, etc
Folks,
I've got an ASP.NET MVC application that I am attempting to secure using the Release Candidate version of ADFS v2.0 (Geneva). I have configured the application as a relying party trust, and I've used Fedutil.exe to modify the application's Web.config so that it has the information about the Geneva server and uses the Geneva server as its claims source.
However, when I try and hit the MVC app, it redirects to Geneva, which then (after warning me about self-signed certs) re-directs me to the MVC app again. After accepting both self-signed cert warnings, the two servers play ping-pong with each other in an infinite redirect loop until finally Geneva spews the following message:
The same client browser session has made '6' requests in the last '1' seconds. There could be a possible bad configuration. Contact your administrator for details.
There are no errors in the event logs on the MVC side or on Geneva except for an event containing the above message. If someone could give me some information on how to try and debug, diagnose, and hopefully fix this problem, I would be eternally grateful.
Again, the Geneva box is the ADFS v2.0 Release Candidate and the ASP.NET MVC site was built using the latest (late '09) version of the Windows Identity Foundation SDK with Web.config modified using FedUtil.exe from the WIF SDK.
So you will all get a kick out of this... I tried this same application from Firefox and ... IT WORKS. I get prompted for my domain credentials, the ADFS v2 server re-directs me ONCE and then I end up on the home page of my application, complete with my account name and personalized greeting.
So now the real issue is this: Why the hell is IE8 getting caught in an infinite redirect loop and Firefox ISN'T ??
After even further testing, I've been able to get this scenario working, out of the box, without modification of any of the default pipeline stuff from ADFS v2 (RC) or from WIF (RTW) on BOTH Safari AND Firefox. IE8 is the ONLY browser to exhibit any problem dealing with this authentication scenario. I've tried everything, including installing and trusting the self-signed certs, adding the sites to my local intranet zone and dropping security to low and even setting first AND third party cookies to always allow.
I had the same issue with ADFS 1.0
And to resolve it, I made sure that the URL had a trailing forward slash "/" that would always work in FireFox as well as IE
eg : https://somedomain.com/Application_2/
Turns out that the host name of the relying party had an underscore in it (khoffman_2). Apparently, the underscore is an illegal DNS character and ONLY IE will reject the information with the underscore in it.
I renamed my machine from khoffman_2 to khoffman2 and the ADFS v2/MVC relying party combination works flawlessly on Firefox, Safari, AND IE.
While this isn't your problem, we have had identical problems to what you described. Our solution was to:
Enabled Basic Authentication in IIS (this solved nothing but was required for the next 2 steps)
Disable Windows Authentication in IIS (this solved the problem for some IE browsers but not all)
Disable Anonymous Access in IIS (this solved the problem for the rest of the IE browsers)
Jaxidian's answer is close.
In my case I only had to:
Windows Authentication -> Disabled
Anonymous Auth -> Enabled
ASP.NET Impersonation -> Disabled
Forms Auth -> Disabled
Windows Auth -> Disabled
This loop can occur when a user is not authorized to access a page.
We had a custom authorization attribute on our MVC controller that checks to see if the user was in a role based on the claims provided if the setting for UseADFS was true in the config files. I thought this setting was set to true and was confounded that I kept getting the adfs loop when accessing the page because I was in the groups that were authorized to access the page.
The key to troubleshooting was to make a web page that displayed my adfs claims without necessarily requiring authentication.
#if (User.Identity.IsAuthenticated)
{
<div>UserName: #User.Identity.Name;</div>
var claimsIdentity = User.Identity as System.Security.Claims.ClaimsIdentity;
<table>
#foreach (var claim in claimsIdentity.Claims)
{
<tr><td>#claim.Type</td><td>#claim.Value</td></tr>
}
</table>
}
I noticed that I was getting logged into ADFS, and my claims were getting set, so ADFS was working. The actual issue was my config file had UserADFS="true" instead of UseADFS="true" which basically caused my custom authorization code to return false on authorization. Therefore, the page kept forwarding me back to adfs to authenticate again.
Anyways, if a user does not have the correct claims to access the page, then this adfs login loop can occur, as well.
Also, if you wrote a custom authorize attribute be sure to check out the following link which describes how to prevent the loop.
Redirect loop with .Net MVC Authorize attribute with ADFS Claims
Custom HandleUnauthorizedRequest handler code for AuthorizeAttribute from that link:
protected override void HandleUnauthorizedRequest System.Web.Mvc.AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAuthenticated)
{
//One Strategy:
//filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
//Another Strategy:
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new
{
controller = "u",
action = "LoginStatus",
errorMessage = "Error occurred during authorization or you do not have sufficient priviliges to view this page."
})
);
}
else
{
base.HandleUnauthorizedRequest(filterContext);
}
}