Fail to connect an ASP.NET Web Client to a Web API 2.0 individual account authentication server - asp.net

I am trying to connect an ASP.NET Single Page Application to a ASP.NET Web API 2.0 server with individual authentication. I am able to authenticate with the server using Fiddler and raw http requests as shown in the below tutorial:
Individual Accounts in Web API
My question is how do I point the separate single page client sample easily to the Web API service? Is it just a simple change in a configuration file or a connection string somewhere?
Surprisingly I cannot find that much info on how to do this on the web probably because it is deceptively simple and I am just missing something because I am new to ASP.NET
Update:
I basically want this to point to a different Web API 2.0 server:
static Startup()
{
PublicClientId = "self";
UserManagerFactory = () => new UserManager<IdentityUser>(new UserStore<IdentityUser>());
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId, UserManagerFactory),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
}
public void ConfigureAuth(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
....
}
That currently points to the root of the application but the Web API 2.0 server is actually on another ASP.NET Application running somewhere else.

This is all code you need to send request to Web Api 2.0:
//line below is for supporting self-generated ssl certificates, you can ommit if you're using http
System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
HttpClientHandler handler = new HttpClientHandler();
//this credentials will be in header
handler.Credentials = new NetworkCredential("someLogin", "somepassword");
client = new HttpClient(handler);
client.BaseAddress = new Uri("http://localhost:4567");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
mediaTypeFormatter = new JsonMediaTypeFormatter();
var content = new ObjectContent<T>(item, mediaTypeFormatter);
var response = await client.PostAsync("api/account/register", content);
if (response.StatusCode != HttpStatusCode.OK && response.StatusCode != HttpStatusCode.BadRequest)
{
throw new ArgumentException("Internal server error");
}
//and here you have successfully sent request and received response from web api.
You can put it into some method then and use method parameters to pass for example host Uri. You can have that saved for example in Web.config as you mentioned.

So after working with the new ASP.NET MVC 5 and the Single Page Application templates in visual studio 2013 I found that the authentication and security is significantly different. This isn't so obvious from any of the documentation.
They both use the "new" ASP.NET Identity which replaces the older membership however when you use the ASP.NET MVC 5 template you get the same old SQL Server type of security setup through the controllers that used to be there, with the SPA (Single Page Application) Template you get API based token authentication.
The only way to wire up the SPA to another API 2.0 located somewhere else is on the client side of things on the JavaScript side, which is slightly confusing because that's not how the ASP.NET MVC 5 template works. The confusion comes from the fact that the SPA gives you both the server side and the client side setup.

Related

Access to Sharepoint Online from Client App

Recently I came up with an issue that I have a .NET Web API which needs to connect to SharePoint Online. In the Azure AD, I have provided all permission to the AppId "AllSites.Manage.All", AllSites.Read.All etc. I used CSOM library to pass the token to the sharepoint. But once I am trying to execute query on the clientcontext received, It is throwing 401 UnAuthorized error
private async Task<ClientContext> GetClientContextWithAccessToken1(string targetUrl)
{
var accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(new string[] {"Files.ReadWrite.All", "Sites.Manage.All", "AllSites.Read"});
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var response = await httpClient.GetAsync($"https://graph.microsoft.com/beta/me");
if(response.StatusCode == HttpStatusCode.OK)
{
var content = await response.Content.ReadAsStringAsync();
}
using(ClientContext clientContext = new ClientContext(targetUrl))
{
clientContext.ExecutingWebRequest +=
delegate (object oSender, WebRequestEventArgs webRequestEventArgs)
{
webRequestEventArgs.WebRequestExecutor.RequestHeaders["Authorization"] =
"Bearer " + accessToken;
};
return clientContext;
}
}
For the request to query https://graph.microsoft.com/beta/me , one of the below permissions is required to be granted consent from admin. like Directory.Read.All , User.Read.All ...
Also please make sure to add Sites.Read.All or Sites.ReadWrite.All Application permission in your registrated AAD Application and do admin consent for it before you getting token to access sharepoint sites.
If you're using v2 endpoint, please go to below URL in your internet browser to do admin grant:
https://login.microsoftonline.com/{yourtenant}/adminconsent?client_id={ applicationid /clientId }&state=123&redirect_uri={redirect uri of your app}
and sign in with Global administrator account and accept this permission.
Reference:
azure-app-cannot-access-sharepoint-online-sites
If you are calling Microsoft Graph API endpoints you should avoid using csom.
AllSites.Manage.All, AllSites.Read.All etc. permissions are related to SharePoint and CSOM and they will not work for Graph API endpoints.
For Graph API you need to acquire different token or better option is to use Microsoft Graph Client Library for .NET in your case.

Integrating ASP.NET code to Active Directory or LDAP after deploying on Bluemix

I'm working on an ASP.Net project, which needs to be deployed after completion on PaaS, which needs to be BlueMix (It wasn't my choice, It was an order).
In addition I need to use an:
Active Directory or LDAP to the User Authentication and Authorization, integrated with the ASP.Net Project.
The Issues Here Are :
1. I have found an integration to the Active Directory or SSO Services using only Java or Node.js, but in my case I am using ASP.Net
2. I want a solution for how the integration can be done on top of the PaaS between the Active Directory and ASP.Net application.
Depending on which version of ADFS you're using, you should be able to use either OAuth or OIDC middleware to connect from an ASP.NET Core application (assuming you're using ASP.NET Core because you're using Bluemix). If you're using at least ADFS 3.0 (Windows Server 2012+), you can use ASP.NET Core's generic OAuth middleware to connect.
First, create a configuration file to store your ADFS server settings, or modify an existing configuration file (such as appsettings.json).
Sample "adfs-settings.json" file:
{
"ADFS": {
"ClientId": "Your ClientId as set on ADFS server",
"ResourceUrl": "url of this application (ex: http://mywebapp.mybluemix.net)",
"ServerUrl": "url of ADFS (ex: https://dc.your.domain)"
}
}
If you created a new file, such as "adfs-settings.json", for your ADFS configuration, add it to your Configuration object in the constructor of your Startup.cs file.
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("adfs-settings.json");
Configuration = builder.Build();
}
In your Configure method of Startup.cs create an OAuthOptions object:
var options = new OAuthOptions();
options.AutomaticChallenge = true;
options.AuthenticationScheme = "ADFS";
Specify the ClientId that you created when configuring this application on your ADFS server by reading it from your Configuration object. The generic OAuth middleware also requires that you provide a ClientSecret here even though that value is not actually used by ADFS 3.0.
options.ClientId = Configuration["ADFS:ClientId"];
options.ClientSecret = "ADFS 3.0 does not support confidential client, but OAuth middleware requires it";
Set the callback url which the ADFS server will redirect to in your application.
options.CallbackPath = new PathString("/signin-adfs");
Now configure the OAuthEvents. OnRedirectToAuthorizationEndpoint defines parameters which are passed to the ADFS authorization endpoint when the application determines that a user needs to be authorized. This will require at least a resource parameter which points to the url of your application. OnCreatingTicket is triggered when the ADFS server has finished authorizing a client and returns a JWT token containing claims data to your application. In this method you'll need to process adding roles and claims to the HttpContext object.
options.Events = new OAuthEvents {
OnRedirectToAuthorizationEndpoint = context =>
{
var parameter = new Dictionary<string, string>
{
["resource"] = Configuration["ADFS:ResourceUrl"]
};
var query = QueryHelpers.AddQueryString(context.RedirectUri, parameter);
context.Response.Redirect(query);
return Task.CompletedTask;
},
OnCreatingTicket = context => {
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
JwtSecurityToken validatedToken = tokenHandler.ReadJwtToken(context.AccessToken);
IEnumerable<Claim> a = validatedToken.Claims;
foreach (var claim in a)
{
// role claim needs to be mapped to http://schemas.microsoft.com/ws/2008/06/identity/claims/role
// for IsInRole() to work properly
if (claim.Type == "role")
{
context.Identity.AddClaim(new Claim(ClaimTypes.Role, claim.Value));
}
else if (claim.Type == "unique_name")
{
// map name to Identity.Name
context.Identity.AddClaim(new Claim(context.Identity.NameClaimType, claim.Value));
}
else
{
// this is optional, if you want any other specific claims from Active Directory
// this will also include some information about the jwt token such as the issue
// and expire times
context.Identity.AddClaim(new Claim(claim.Type, claim.Value));
}
}
return Task.CompletedTask;
}
};
Next, set the ClaimsIssuer to the ADFS url and set the SignInScheme to CookieAuthenticationDefaults.AuthenticationScheme and configure the AuthorizationEndpoint and TokenEndpoint to the proper endpoints on your ADFS server.
options.ClaimsIssuer = Configuration["ADFS:ServerUrl"];
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.AuthorizationEndpoint = Configuration["ADFS:ServerUrl"] + "/adfs/oauth2/authorize/";
options.TokenEndpoint = Configuration["ADFS:ServerUrl"] + "/adfs/oauth2/token/";
Finally, add the OAuth middleware using the options you've just created:
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOAuthAuthentication(options);
Now you should be able to apply the [Authorize] attribute to any controller or action which requires authorization with ADFS. For a complete sample application see this GitHub repo.

Replacing Cookie by Token based authentication in ASP.NET OWIN OpenIdConnect code authorization flow

We have a web application written in ASP.NET that uses MVC for serving our Single Page Applications and Web API for ajax calls.
The authentication uses Microsoft.Owin and OpenIdConnect with Azure AD for Authority. The OAUTH flow is server side code authorization.
Then in Startup.Auth.cs we have
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
var cookieAuthenticationOptions = new CookieAuthenticationOptions()
{
CookieName = CookieName,
ExpireTimeSpan = TimeSpan.FromDays(30),
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
SlidingExpiration = true,
};
app.UseCookieAuthentication(cookieAuthenticationOptions);
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
AuthorizationCodeReceived = (context) =>
{
/*exchange authorization code for a token
stored on database to access API registered on AzureAD (using ADAL.NET) */
},
RedirectToIdentityProvider = (RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context) =>
{
/* Set the redirects URI here*/
},
});
}
When clicking on signin we navigate to an url whose routes map to the methods of the following MVC controller
public class AccountController : Controller
{
public void SignIn(string signalrRef)
{
var authenticationProperties = /* Proper auth properties, redirect etc.*/
HttpContext.GetOwinContext()
.Authentication.Challenge(authenticationProperties, OpenIdConnectAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType);
}
public void SignOut(string signalrRef)
{
var authenticationProperties = /* Proper auth properties, redirect etc.*/
HttpContext.GetOwinContext().Authentication.SignOut(authenticationProperties,
OpenIdConnectAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType);
}
Then the end-user connected to our application is authenticated between our client apps and the ASP.net server using an ASP.NET cookie. We would like to use Token Based approach instead. If you are interested this is the reason.
I tried to replace
the Nuget package Microsoft.Owin.Security.Cookies by Microsoft.Owin.Security.OAuth and in Startup.cs
replacing
app.UseCookieAuthentication(cookieAuthenticationOptions); by app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
and in my AccountController we changed the challenge from HttpContext.GetOwinContext().Authentication.SignOut(authenticationProperties,
OpenIdConnectAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType); to HttpContext.GetOwinContext().Authentication.SignOut(authenticationProperties,
OpenIdConnectAuthenticationDefaults.AuthenticationType, OAuthDefaults.AuthenticationType);
The problem is that with Cookie the set-cookie was automatically sent in web request respond when the flow completes while redirecting to the url we specified.
Where can I find the Bearer generated by OWIN with UseOAuthBearerAuthentication (if there is any) **, **Where and When should I send it back to my client SPAs
Note: an open source sample of what we are trying to do can be found in this github repository.
I think there are two approaches for you to consider.
Use javascript libraries to perform sign-in & token acquisition within your single page app. Then your backend is purely an web API, and can just use OAuth bearer middleware to authenticate requests. The backend doesn't know anything about signing the user in. We have a good sample that takes this approach here. If your backend needs to make API calls as well, you could consider the OnBehalfOf flow as well. I usually recommend this approach.
Use the OpenIDConnect middleware in your server to perform user sign-in and token acquisition. You might even be able to omit the usage of the CookieAuthenticationMiddleware entirely (although I'm not 100% sure). You can capture the token in the AuthorizationCodeReceived notification as you mention, and you could redirect back to your SPA with the token in the fragment of the URL. You could also have some route which delivers the tokens (which are cached on your server) down to your javascript. In either case, you'll need to ensure that an outside caller can't get access to your tokens.
The thing to keep in mind will be how you refresh tokens when they expire. If you use #1, most of it will be handled for you by libraries. If you use #2, you'll have to manage it more yourself.

How should I be handling authentication with Identity 2.0 and WebAPI 2.1 and Owin 2?

I'm using
New browser only clients on the same domain
Identity 2.0
WebAPI 2.1
Owin 2.1
AngularJS front-end for registration, login and data display
In a WebAPI application with an AngularJS front-end.
I'm reading about token authentication but I am very confused now and I cannot find any good examples out there that use my combination. What I would like to know is should I be using cookies or tokens for the authentication. Should I be using a Userfactory or the CreatePerOwinContext?
Here's what I have in my Startup.Auth.cs
public partial class Startup {
public void ConfigureAuth(IAppBuilder app) {
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/"),
Provider = new CookieAuthenticationProvider {
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
// Enables the application to remember the second login verification factor such as phone or email.
// Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
// This is similar to the RememberMe option when you log in.
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
}
}
Here's my WebAPI config:
public static class WebApiConfig
{
public static void CustomizeConfig(HttpConfiguration config)
{
config.Formatters.Remove(config.Formatters.XmlFormatter);
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
json.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
json.SerializerSettings.Converters.Add(new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-ddTHH:mmZ" });
}
I saw some examples using this code but I am not sure how I can call this:
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId, UserManagerFactory),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
Could I just replace the cookie authentication with this?
Not an expert, but in my dabbling I've found that tokens work great for api and from javascript to api, and traditional cookies lean mostly for a ui. Either or both will work depending on what your trying to do.
You can follow something like this link that does cookie for the ui and token for the api http://blog.iteedee.com/2014/03/asp-net-identity-2-0-cookie-token-authentication/
app.CreatePerOwinContext(ApplicationSession.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Token Authentication
app.UseOAuthBearerAuthentication(new OAuthBearerOptions());
I think you can set the cookie authentication options authentication type to bearer if you want bearer for both, but you would have to play with it. The token would be in the owincontext under ".AspNet.ExternalBearer".
I also think if you register the Identity 2.0 middleware i think it also registers the oauth middleware stuff so you don't need to register the oauthserver middleware yourself. Thats the OAuthAuthorizationServerOptions code you posted. You dont need it.
if the ui and api are in separate then its a bit harder if you want to do some sort of single sign on from the ui pass to the api. I would recommend looking at opensource identity server or authorization server from thinktecture.
If your set on owin middleware and Identity 2.0 you would need to make sure the token can be read by both application and api and you probably would need to implement ISecureDataFormat. But remember, decryption doesn't mean you can 100% trust a token, it should be signed and verified. Depends on your needs.
Sorry, I guess thats a long ramble... Good luck.

How to call WCF Http Service behind SiteMinder

I am trying to call WCF 4 Http Web Services which are hosted within an ASP.NET application. The Service is protected behind SiteMinder.
I was wondering how I could programmatically call the web service, and more specifically what information will I need to pass to be authorized within SiteMinder to access my resources.
I am making the request from the ASP.NET application running on the same server, so I have access to the authentication cookie.
First obtain the SiteMinder authentication token like so:
private string ObtainSiteMinderSession()
{
var cookie = Request.Cookies["SMSESSION"];
return cookie != null ? cookie.Value : string.Empty;
}
Then pass this token as with your web service calls like so (using Microsoft.Http.dll):
using Microsoft.Http;
using Microsoft.Http.Headers;
...
var Client = new HttpClient(baseUri);
// Add SMSESSION
var smCookie = new Cookie();
smCookie.Add("SMSESSION", ObtainSiteMinderSession());
Client.DefaultHeaders.Cookie.Add(smCookie);
using (var httpRequest = new HttpRequestMessage(Verbs.GET, "/LoadData/"))
{ ... }

Resources