Dynamically set OWIN redirect uri - c#

I am using OWIN to connect up to O365 via the Microsoft Graph API in an ASP.NET MVC app. Everything is setup in Startup.Auth.cs including the Redirect Uri value which currently comes from the web.config. Authentication is working correctly.
As I am using wildcards in the App Registration, the redirect uri can be a variety of values and te user is able authenticate to O365 from any number of pages in the app. Once authenticated I'd like them to be brought back to the page they were just on but because the redirect uri is already set, they are brought back to that page.
How can I modify the redirect uri, elsewhere in my code, after the OWIN identity context has been created?
Below is a snippet of the startup code.
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
private static string redirectUri = ConfigurationManager.AppSettings["ida:RedirectUri"];
....
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = appId,
Authority = "https://login.microsoftonline.com/organizations/v2.0",
PostLogoutRedirectUri = redirectUri,
RedirectUri = redirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async (context) =>
{
Dictionary<string, string> data = new Dictionary<string, string>();
data.Add("client_id", appId);
data.Add("client_secret", appSecret);
data.Add("code", code);
data.Add("grant_type", "authorization_code");
data.Add("redirect_uri", redirectUri);
...

I had a similar situation. I tied into RedirectToIdentityProvider, to modify the RedirectUri before sending the request to the identity provider. Something like the following
Notifications = new OpenIdConnectAuthenticationNotifications()
{
RedirectToIdentityProvider = async (context) =>
{
context.ProtocolMessage.RedirectUri = "Whatever_You_Want_Here";
}
}

I wanted to dynamically determine the redirect_uri of the application and came up with this solution with the help of above answers and comments:
Notifications = new OpenIdConnectAuthenticationNotifications()
{
RedirectToIdentityProvider = (o) =>
{
o.ProtocolMessage.RedirectUri = DetermineRedirectUri(o.Request);
return Task.CompletedTask;
},
AuthorizationCodeReceived = (o) =>
{
o.TokenEndpointRequest.RedirectUri = DetermineRedirectUri(o.Request);
return Task.CompletedTask;
}
}
And the helper method:
private string DetermineRedirectUri(IOwinRequest request)
{
return request.Scheme + System.Uri.SchemeDelimiter + request.Host + request.PathBase;
}

For anyone coming here and wondering where they should put this, it still goes into your statup.auth.cs (or similar) file. Where you need to put it is here:
public void ConfigureAzureAuth(IAppBuilder app)
{
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12
| SecurityProtocolType.Ssl3;
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// Sets the ClientId, authority, RedirectUri as obtained from web.config
Caption = "Your site",
AuthenticationType = "Your site",
ClientId = clientId,
Authority = authority,
//MetadataAddress = "https://your auth url/.well-known/openid-configuration",
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 = "/",
Scope = OpenIdConnectScope.OpenIdProfile,
// ResponseType is set to request the code id_token - which contains basic information about the signed-in user
ResponseType = OpenIdConnectResponseType.CodeIdToken,
// OpenIdConnectAuthenticationNotifications configures OWIN to send notification of failed authentications to OnAuthenticationFailed method
Notifications = new OpenIdConnectAuthenticationNotifications
{
//These are like call backs, so they fire when the event happens.
AuthenticationFailed = OnAuthenticationFailed,
//We can tap into this notification to get the IOwinRequest object
RedirectToIdentityProvider = (o) =>
{
//here is where we set the redirect Uri based on the request we received.
o.ProtocolMessage.RedirectUri = DetermineRedirectUri(o.Request);
return Task.CompletedTask;
},
AuthorizationCodeReceived = (o) =>
{
o.TokenEndpointRequest.RedirectUri = DetermineRedirectUri(o.Request);
return Task.CompletedTask;
}
},
});
}
You will also need the additional method that #brz included.
private string DetermineRedirectUri(IOwinRequest request)
{
return request.Scheme + System.Uri.SchemeDelimiter + request.Host + request.PathBase + "/Account/ExternalLoginCallback/";
}

Related

Refresh access token after expiry in ASP.NET MVC

I'm Fetching access token in ASP.NET MVC application using Open ID connect hybrid flow. And using this access token to invoke Power BI Rest APIs. However once the access token has expired, the REST API calls fails for obvious reasons.
My question is How do I get the new access token/refresh without pushing user for interactive login?
public void ConfigureAuth(IAppBuilder app)
{
try
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
RedirectUri = redirectUri,
UseTokenLifetime = false,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect("/Error?message=" + context.Exception.Message);
return Task.FromResult(0);
},
AuthorizationCodeReceived = OnAuthorizationCodeCallback
}
});
app.UseStageMarker(PipelineStage.Authenticate);
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
private static async Task OnAuthorizationCodeCallback(AuthorizationCodeReceivedNotification context)
{
var appConfidential = ConfidentialClientApplicationBuilder.Create(clientId)
.WithRedirectUri(redirectUri)
.WithClientSecret(clientSecret)
.WithAuthority(authority)
.Build();
string powerBiPermissionApi = "https://analysis.windows.net/powerbi/api/";
string[] ReadUserWorkspaces = new string[] {
powerBiPermissionApi + "Workspace.Read.All",
powerBiPermissionApi + "Report.Read.All",
powerBiPermissionApi + "Dashboard.Read.All",
powerBiPermissionApi + "Dataset.Read.All"
};
var authResult = await appConfidential.AcquireTokenByAuthorizationCode(ReadUserWorkspaces, context.Code).ExecuteAsync();
ClaimsIdentity userClaims = context.AuthenticationTicket.Identity;
userClaims.AddClaim(new Claim("Access_Token", authResult.AccessToken));
}
With Azure Active Directory, we may specify our own custom timeout for measured days / Decouple the session length from the token validity.
One of the approaches I found is to separate the session duration from the original token's expiration times. By supplying the following option to the OIDC Middleware, you can tell it to stop controlling this aspect in the cookie Middleware:
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions {
...
UseTokenLifetime = false,
}
);
The cookie Middleware will now follow whatever settings you provide in the cookie Middleware parameters if UseTokenLifetime is set to false.
Alternatively, we can use an iFrame from a page that updates every 5 minutes.
<iframe sandbox="allow-same-origin allow-scripts allow-popups allow-forms" id="refreshAuthenticationIframe" src="#Url.Action("CheckSessionTimeout", "Home", new { area = "" })" style="display:none;"></iframe>
You find additional details through this Thread.
REFERENCES:
Controlling a Web App’s session duration – CloudIdentity
Solved: How to use Power BI Rest API without GUI authentic... - Microsoft Power BI Community

Request.IsAuthenticated is always false after logging into Microsoft AD

I have an ASP.NET 4.5.1 MVC/WebAPI project. It authenticates against Microsoft using SSO. After successfully logging into Microsoft, I still have Request.IsAuthenticated on my HomeController. The url I have registered to web config file is http://localhost:58686/
Now, I have a sample MVC only application that successfully authenticates after login. I have double, triple checked the code and my mvc/Web Api project uses the same startup.cs, same web config structure and same registration to azure ad portal. But in this particular project, after logging into microsoft, request.isauthentciated is always false. However, I get all the right claims from the context parameter on SecurityTokenValidated. Do you have any idea about why it is happening? Because I set up the project as MVC/WebAPI?
Home Controller:
public ActionResult Index()
{
//var y = HttpContext.User.Identity.IsAuthenticated;
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = System.Configuration.ConfigurationManager.AppSettings["redirectUrl"] },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
Startup.cs
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUrl,
PostLogoutRedirectUri = redirectUrl,
Scope = OpenIdConnectScopes.OpenIdProfile,
ResponseType = OpenIdConnectResponseTypes.IdToken,
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters() { ValidateIssuer = false },
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
RedirectToIdentityProvider = (context) =>
{
string appBaseUrl = context.Request.Scheme + "://" + context.Request.Host + context.Request.PathBase;
context.ProtocolMessage.RedirectUri = appBaseUrl + "/";
context.ProtocolMessage.PostLogoutRedirectUri = appBaseUrl;
return Task.FromResult(0);
},
SecurityTokenValidated = (context) =>
{
var identity = context.AuthenticationTicket.Identity;
return Task.FromResult(0);
}
}
}
);
Thank you and Kind Regards!
Request.IsAuthenticated is always FALSE when you deal with <authentication mode="Forms"> and always TRUE if you deal with <authentication mode="Windows" />. Please double check your web.config and remove this setting. After that the Request.IsAuthenticated is now defend on your AzureAD.

ADAL bearer token does not return username but aad client id

We have an desktop app which uses adal to authenticate the user, using this code:
AuthenticationResult result = null;
var context = new AuthenticationContext(aadTenantDomain);
result = await context.AcquireTokenAsync(resourceId, clientId, returnUrl, new PlatformParameters(PromptBehavior.Auto));
This works fine and the returned AuthenticationResult has all the right user information. Now we call a web app web api controller hosted on azure with the access token obtained from AuthenticationResult:
var Client = new HttpClient();
Client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", result.AccessToken);
Which also correctly authorizes the user. Now in the web controller we use User.Identity.Name to get the username which was authorized by the access token. For many month til yesterday this worked well, but today User.Identity.Name returns the client id of the desktop app instead of a username. Anyone knows what migh be wrong?
This is the api auth configuration:
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
RoleClaimType = System.Security.Claims.ClaimTypes.Role,
}
});
}
And this is an example controller function:
[Authorize]
public class dialplanController : ApiController
{
public async Task<IHttpActionResult> GetMe()
{
var Me = db.dialplan.FirstOrDefault(d => d.email == User.Identity.Name);
return Ok(Me);
}
}
If you want the web app support both OpenIdConnection and Windows Azure Active Directory bearer token, you need to add the code app.UseWindowsAzureActiveDirectoryBearerAuthentication() as juunas mentioned.
For example, here is the code for your reference:
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,
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,
RedirectToIdentityProvider= OnRedirectToIdentityProvider,
MessageReceived= OnMessageReceived,
SecurityTokenReceived= OnSecurityTokenReceived,
},
});
app.UseWindowsAzureActiveDirectoryBearerAuthentication(new Microsoft.Owin.Security.ActiveDirectory.WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = "",
Tenant = "",
});
}
After that we can call the web API using the token acquire from Azure AD. And the Azure Active Directory OWIN component will transform the User.Identity.Name from delegate-token based on the unique_name claim in the access_token.
Please decode the access token from this site to see whether the unique_name is expected.
Using ClaimsPrincipal.Current?.FindFirst(ClaimTypes.Upn)?.Value returns the correct UPN of the current user or null if no upn is given.

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"
});

How to write OAuth2 Web API Client in Asp.net MVC

We have developed a set of Web APIs (REST) which are protected by an Authorization server. The Authorization server has issued the client id and client secret. These can be used to obtain an access token. A valid token can be used on subsequent calls to resource servers (REST APIs).
I want to write an web based (Asp.net MVC 5) client that will consume the APIs. Is there a nuget package I can download that will help me to implement the client OAuth2 flow? Can anyone direct me to a good example on client implementation of OAuth2 flow (written in asp.net MVC)?
Update
I was able to get access token using the code block below, but what I want is a "client credentials" oauth 2 flow where I don't have to enter login and passwords. The code I have now is:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType("ClientCookie");
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AuthenticationType = "ClientCookie",
CookieName = CookieAuthenticationDefaults.CookiePrefix + "ClientCookie",
ExpireTimeSpan = TimeSpan.FromMinutes(5)
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AuthenticationType = OpenIdConnectAuthenticationDefaults.AuthenticationType,
SignInAsAuthenticationType = app.GetDefaultSignInAsAuthenticationType(),
ClientId = ConfigurationManager.AppSettings["AuthServer:ClientId"],
ClientSecret = ConfigurationManager.AppSettings["AuthServer:ClientSecret"],
RedirectUri = ConfigurationManager.AppSettings["AuthServer:RedirectUrl"],
Configuration = new OpenIdConnectConfiguration
{
AuthorizationEndpoint = "https://identityserver.com/oauth2/authorize",
TokenEndpoint = "https://identityserver.com/oauth2/token"
},
//ResponseType = "client_credentials", // Doesn't work
ResponseType = "token",
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = notification =>
{
if (string.Equals(notification.ProtocolMessage.Error, "access_denied", StringComparison.Ordinal))
{
notification.HandleResponse();
notification.Response.Redirect("/");
}
return Task.FromResult<object>(null);
},
AuthorizationCodeReceived = async notification =>
{
using (var client = new HttpClient())
{
//var configuration = await notification.Options.ConfigurationManager.GetConfigurationAsync(notification.Request.CallCancelled);
String tokenEndPoint = "https://identityserver.com/oauth2/token";
//var request = new HttpRequestMessage(HttpMethod.Post, configuration.TokenEndpoint);
var request = new HttpRequestMessage(HttpMethod.Post, tokenEndPoint);
request.Content = new FormUrlEncodedContent(new Dictionary<string, string> {
{ OpenIdConnectParameterNames.ClientId, notification.Options.ClientId },
{ OpenIdConnectParameterNames.ClientSecret, notification.Options.ClientSecret },
{ OpenIdConnectParameterNames.Code, notification.ProtocolMessage.Code },
{ OpenIdConnectParameterNames.GrantType, "authorization_code" },
{ OpenIdConnectParameterNames.RedirectUri, notification.Options.RedirectUri }
});
var response = await client.SendAsync(request, notification.Request.CallCancelled);
response.EnsureSuccessStatusCode();
var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
// Add the access token to the returned ClaimsIdentity to make it easier to retrieve.
notification.AuthenticationTicket.Identity.AddClaim(new Claim(
type: OpenIdConnectParameterNames.AccessToken,
value: payload.Value<string>(OpenIdConnectParameterNames.AccessToken)));
}
}
}
});
}
}
To support the client credentials grant type, your best option is probably to directly use HttpClient:
var request = new HttpRequestMessage(HttpMethod.Post, "http://server.com/token");
request.Content = new FormUrlEncodedContent(new Dictionary<string, string> {
{ "client_id", "your client_id" },
{ "client_secret", "your client_secret" },
{ "grant_type", "client_credentials" }
});
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
var token = payload.Value<string>("access_token");
For interactive flows (like the authorization code flow), there are two better approaches:
If your authorization server supports OpenID Connect (which is based on OAuth2), you can simply use the OpenID Connect middleware for OWIN/Katana 3 developed by Microsoft: https://www.nuget.org/packages/Microsoft.Owin.Security.OpenIdConnect/
If OpenID Connect is not supported by your authorization server, one option is to create your own OAuth2 client middleware. You can take a look at the last part of this SO answer for more information: Registering Web API 2 external logins from multiple API clients with OWIN Identity

Categories