ADFS 2.0 Windows 2008 R2 Web API - asp.net-mvc

I would like to make a MVC Web Application that talks to a Web API application and use ADFS 2.0 (on Windows 2008 R2) for authentication.
I managed to make the MVC Web Application to authenticate using ADFS.
Q: But I don't know how I should federate my ADFS 2.0 (on Windows 2008 R2) from MVC Web to Web API (assuming they will be deployed in separate servers)?
I found a lot of articles on how to do this with WCF or Windows Server 2012 R2, but not with Web API and ADFS 2.0 in Windows Server 2008 R2
Edit, In the end I went for poor man delegation(passing the same token that I receive to the front end to the backend (as it would not make sense to call the adfs again)
FrontEnd -> Call GetToken and put in on the authorization header (I encode it to base64)
public string GetToken()
{
BootstrapContext bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext as BootstrapContext;
string token = bootstrapContext.Token;
if (string.IsNullOrEmpty(token))
token = ToTokenXmlString(bootstrapContext.SecurityToken as SamlSecurityToken);
return token;
}
string ToTokenXmlString(SecurityToken token)
{
var genericToken = token as GenericXmlSecurityToken;
if (genericToken != null)
return genericToken.TokenXml.OuterXml;
var handler = SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();
return ToTokenXmlString(token, handler);
}
string ToTokenXmlString(SecurityToken token, SecurityTokenHandlerCollection handler)
{
if (!handler.CanWriteToken(token))
throw new InvalidOperationException("Token type not suppoted");
var sb = new StringBuilder(128);
using (StringWriter stringWriter = new StringWriter(sb))
{
using (var textWriter = new XmlTextWriter(stringWriter))
{
handler.WriteToken(textWriter, token);
return sb.ToString();
}
}
}
Backend-> Parse and validate the token->
public ClaimsIdentity GetIdentityFromToken(string tokenBase64)
{
if (string.IsNullOrEmpty(tokenBase64))
return null;
byte[] tokenByteArray = Convert.FromBase64String(tokenBase64);
string decodedToken = Encoding.UTF8.GetString(tokenByteArray);
if (string.IsNullOrWhiteSpace(decodedToken))
return null;
try
{
var handlers = FederatedAuthentication.FederationConfiguration.IdentityConfiguration.SecurityTokenHandlers;
SecurityToken token;
using (StringReader stringReader = new StringReader(decodedToken))
{
using (XmlTextReader xmlReader = new XmlTextReader(stringReader))
{
token = handlers.ReadToken(xmlReader);
}
}
if (token == null)
return null;
return handlers.ValidateToken(token).FirstOrDefault();
}
catch (Exception e)
{
logger.Error(new AuthenticationException("Error validating the token from ADFS", e));
return null;
}
}

I implemented this by passing the bearer token that I received from Adfs into the authorization header of the web api call, and then using the Microsoft.Owin.Security.Jwt nuget package to translate the token into the httpcontext current identity during owin startup in the web api project.
This example uses a jwt token as the bearer token. Choose the proper NuGet package for the token type that you want to use.
Construct the WebRequest in mvc controller
BootstrapContext bc = ClaimsPrincipal.Current.Identities.First().BootstrapContext as BootstrapContext;
HttpWebRequest request = WebRequest.Create(ConfigurationManager.AppSettings["ApiUrl"]) as HttpWebRequest;
request.Method = "GET";
request.Headers["Authorization"] = "Bearer " + bc.Token;
Owin Startup.cs file in web api Before the app.UseWebApi(config) line.
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { ConfigurationSettings.AppSettings["ida:Realm"] },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(
ConfigurationSettings.AppSettings["ida:ValidIssuer"],
ConfigurationSettings.AppSettings["ida:SymmetricKey"])
},
Provider = new OAuthBearerAuthenticationProvider
{
OnValidateIdentity = context =>
{
return System.Threading.Tasks.Task.FromResult<object>(null);
}
}
});

Related

Authorized Web API Access from MVC

I am facing some issue with Azure AD authentication.
My application architecture is Asp.net MVC Web & Web API as middle ware
when i am trying to authenticate using AD Token at web API from MVC, i am not able to get any error and even no response from WEB API in Code
But if i try accessing the API using browser where i have already used credentials for Authenticating to MVC app it works fine.
Below is the code to access API but it didn't worked
AuthenticationResult result = null;
string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
string ApiClientId = ConfigurationManager.AppSettings["ida:ApiClientId"];
string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
string tenantId = ConfigurationManager.AppSettings["ida:TenantId"];
string postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:ApplicationURI"];
string postLoginRedirectUri = ConfigurationManager.AppSettings["ida:RedirectUri"];
string clientSecret = ConfigurationManager.AppSettings["ida:ClientSecret"];
string authority = aadInstance + tenantId;
IConfidentialClientApplication app = MsalAppBuilder.BuildConfidentialClientApplication();
var account = await app.GetAccountAsync(ClaimsPrincipal.Current.GetMsalAccountId());
string[] scopes = { "openid profile offline_access email User.Read" };
try
{
// try to get an already cached token
result = await app.AcquireTokenSilent(scopes, account).ExecuteAsync().ConfigureAwait(false);
}
catch (MsalUiRequiredException ex)
{
{
// A MsalUiRequiredException happened on AcquireTokenSilentAsync.
// This indicates you need to call AcquireTokenAsync to acquire a token
//Debug.WriteLine($"MsalUiRequiredException: {ex.Message}");
try
{
// Build the auth code request Uri
string authReqUrl = await OAuth2RequestManager.GenerateAuthorizationRequestUrl(scopes, app, this.HttpContext, Url);
}
catch (MsalException msalex)
{
Response.Write($"Error Acquiring Token:{System.Environment.NewLine}{msalex}");
}
}
var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
handler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls;
HttpClient client = new HttpClient(handler);
//apiUrl client.BaseAddress = apiUrl;
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, apiUrl + "api/General/GetUserDetailsByEmailAddress?emailAddress=ikhlesh.saxena#amexassetmanagement.com");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
try
{
HttpResponseMessage response = await client.SendAsync(request);
}
catch (Exception ex)
{
}
Check if your scopes allow you to access your API. Also, you need to debug on the API side whether the token is coming with a request, and if yes how it's validated.

I want to use office 365 Api for my Asp.net with C# non mvc Project

Hello Folks I am new to using API into my project.
I am using Asp.Net with C# which does not have MVC architecture.
My client needed to integrate office 365 API into the project so that any user who want to access our service can login through their office 365 credentials.
while I searched on internet about the sources it said I needed ASP.net with MVC to use office 365 . Please suggest what could be done.
You could use Active Directory Authentication Library .NET to easily authenticate users to cloud or on-premises Active Directory (AD), and then obtain access tokens for securing API calls . In web form , code below is for your reference :
protected void Page_Load(object sender, EventArgs e)
{
string authCode = Request.Params["code"];
if (!string.IsNullOrEmpty(authCode))
{
Authorize(authCode);
}
string token = (string)Session["access_token"];
if (string.IsNullOrEmpty(token))
{
return;
}
try
{
// get user name
getUserName(token);
}
catch (AdalException ex)
{
}
}
public void getUserName(string token)
{
using (var client = new HttpClient())
{
//Enable signon and read users' profile
using (var request = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/beta/me"))
{
request.Headers.Add("Authorization", "Bearer " + token);
request.Headers.Add("Accept", "application/json;odata.metadata=minimal");
using (var response = client.SendAsync(request).Result)
{
if (response.StatusCode == HttpStatusCode.OK)
{
var json = JObject.Parse(response.Content.ReadAsStringAsync().Result);
Response.Write(json["displayName"].ToString());
}
}
}
}
}
public void Authorize(string authCode) {
AuthenticationContext authContext = new AuthenticationContext("https://login.microsoftonline.com/common");
// The same url we specified in the auth code request
string redirectUri = "http://localhost:55065/Default.aspx";
// Use client ID and secret to establish app identity
ClientCredential credential = new ClientCredential(ConfigurationManager.AppSettings["ClientID"], ConfigurationManager.AppSettings["ClientSecret"]);
try
{
// Get the token
var authResult = authContext.AcquireTokenByAuthorizationCode(
authCode, new Uri(redirectUri), credential, "https://graph.microsoft.com/");
// Save the token in the session
Session["access_token"] = authResult.AccessToken;
Response.Redirect(redirectUri.ToString());
}
catch (AdalException ex)
{
//return Content(string.Format("ERROR retrieving token: {0}", ex.Message));
}
}
public void signin()
{
var authContext = new AuthenticationContext("https://login.microsoftonline.com/common");
// The url in our app that Azure should redirect to after successful signin
string redirectUri = "http://localhost:55065/Default.aspx";
// Generate the parameterized URL for Azure signin
Uri authUri = authContext.GetAuthorizationRequestURL("https://graph.microsoft.com/", ConfigurationManager.AppSettings["ClientID"],
new Uri(redirectUri), UserIdentifier.AnyUser, null);
// Redirect the browser to the Azure signin page
Response.Redirect(authUri.ToString());
}
You could also refer to below link to get some examples with O365 API in GitHub :
https://github.com/dream-365/OfficeDev-Samples/tree/master/samples/Office365DevQuickStart
But I would suggest you could try to use ASP.NET MVC , it is designed with separation of concerns and testability in mind , and you will find a lot of MVC samples with O365 in here:
Office 365 API code samples and videos

mvc and web.api authorisation using oauth

Seems like I am asking the same question that many have but can not fit everything together.
I have a standard MVC application configured with Oauth using the standard Identity DB, the user signs in and that all works fine, I then want to call the Web.API application as an authenticated user. From my research I have added the bearer token to the httpclient thinking that this would somehow be authorized in the web.api application I have set the Web.API application to point to the same identity db but im not sure what i am missing. I've spent days on this and no luck so any samples would be much appreciated.
the code to get the bearer token is
protected string GetToken()
{
var identity = new ClaimsIdentity(Startup.OAuthBearerOptions.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, User.Identity.Name));
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, User.Identity.GetClaimValue(ClaimTypes.NameIdentifier.ToString())));
AuthenticationTicket ticket = new AuthenticationTicket(identity, new AuthenticationProperties());
var currentUtc = new SystemClock().UtcNow;
ticket.Properties.IssuedUtc = currentUtc;
ticket.Properties.ExpiresUtc = currentUtc.Add(TimeSpan.FromMinutes(30));
string accessToken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);
return accessToken;
}
the call to the Web.APi is
var token = GetToken();
string uri = UriEASOnlineApi + EASOnlineWebAPI.SignErectors;
List<SignErector> result;
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
var task = await client.GetAsync(uri);
if (task.IsSuccessStatusCode)
{
var jsonString = await task.Content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<List<SignErector>>(jsonString);
}
else
{
throw new Exception($"failed: {task.StatusCode}");
}
}
First of all you need to know how authentication is done in WebApi. Then if you have working example of call to this WebApi e.g. from Postman or Fiddler you can compare manual request with the one you create programmatically from MVC application.

Thinktecture Identity server v3 - Facebook Assertion Flow

Is there a possibility to configure OAuth2 AssertionFlow with Facebook in Thinktecture Identity Server v3?
There was a post on leastprivilege.com about implementing AssertionFlow for Microsoft OAuth and AuthorizationServer but I need to integrate with Facebook and, furthermore, AuthorizationServer is marked as deprecated and it's not maintained anymore.
In response to #NathanAldenSr's comment, I publish some code of my working solution.
Server side - custom validator:
public class FacebookCustomGrantValidator: ICustomGrantValidator
{
private readonly IUserService userService;
private const string _FACEBOOK_PROVIDER_NAME = "facebook";
// ...
async Task<CustomGrantValidationResult> ICustomGrantValidator.ValidateAsync(ValidatedTokenRequest request)
{
// check assetion type (you can have more than one in your app)
if (request.GrantType != "assertion_fb")
return await Task.FromResult<CustomGrantValidationResult>(null);
// I assume that fb access token has been sent as a response form value (with 'assertion' key)
var fbAccessToken = request.Raw.Get("assertion");
if (string.IsNullOrWhiteSpace(assertion))
return await Task.FromResult<CustomGrantValidationResult>(new CustomGrantValidationResult
{
ErrorMessage = "Missing assertion."
});
AuthenticateResult authebticationResult = null;
// if fb access token is invalid you won't be able to create Facebook client
var client = new Facebook.FacebookClient(fbAccessToken);
dynamic response = client.Get("me", new { fields = "email, first_name, last_name" });
// create idsrv identity for the user
authebticationResult = await userService.AuthenticateExternalAsync(new ExternalIdentity()
{
Provider = _FACEBOOK_PROVIDER_NAME,
ProviderId = response.id,
Claims = new List<Claim>
{
new Claim("Email", response.email),
new Claim("FirstName", response.first_name),
new Claim("LastName", response.last_name)
// ... and so on...
}
},
new SignInMessage());
return new CustomGrantValidationResult
{
Principal = authebticationResult.User
};
}
}
You can easily test it with OAuth2Client that is also provided by Thinktecture (in Thinktexture.IdentityModel Client Library nuget package).
string fbAccessToken = "facebook_access_token_you_aquired_while_logging_in";
string assertionType = "assertion_fb";
var client = new OAuth2Client(
new Uri("your_auth_server_url"),
"idsrv_client_id",
"idsrv_client_secret");
string idsrvAccessToken = client.RequestAssertionAsync(assetionType, fbAccessToken,).Result;
IdentityServer v3 also supports assertion flow. The samples wiki has two samples on that (called "Custom Grants):
https://github.com/thinktecture/Thinktecture.IdentityServer.v3.Samples/tree/master/source

MVC 5 application - implement OAuth Authorization code flow

Based on this tutorial http://www.asp.net/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server, I have created an Authorization Server, a Resource Server and a MVC Client.
The MVC Client has a Controller which gets some data from the Resource Server. The Resource Server requires authentication. The MVC Clients gets an authorization code from the Authorization Server and Redirects the user to the Authorization Server for authentication. Finally the MVC Clients exchanges the authorization code for a Access token to Access the Resource Server. This is the Authorization code flow as described by the OAuth 2 protocol. This works fine.
Now, I have the requirement to make a Controller of the MVC Client itself require Authentication. I can not find a tutorial for this.
I added
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
to my Startup.Auth.cs.
I assume, I need to setup the Options to Redirect to the Authorization Server. I can also set the Provider on the Options:
app.UseOAuthBearerAuthentication(new Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationOptions()
{
Provider = new OAuthBearerAuthenticationProvider()
});
But I am also stuck on implementing the events of the Provider.
Can anybody guide me in the right direction? Or are there any tutorials which might help me?
I ended up with a solution based on these two articles from Brock Allen:
http://brockallen.com/2013/10/24/a-primer-on-owin-cookie-authentication-middleware-for-the-asp-net-developer/
http://brockallen.com/2014/01/09/a-primer-on-external-login-providers-social-logins-with-owinkatana-authentication-middleware/
The fundemental idea is to register two authentication Middlewares. An active Cookie-Authentication and a passive OAuthBearer-Authentication. In Startup.Auth.cs they are added like this:
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/ExternalLogin/Login"),
});
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
AuthenticationType = DefaultAuthenticationTypes.ExternalBearer,
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Passive,
});
You also add an ExternalLogin-Controller. Its Login-method has to redirect the user to the Login-page of your Authorization Server to get the authorization code. You have to supply a callback function where you will process the authorization code.
public async Task<ActionResult> Login(string returnUrl)
{
if (string.IsNullOrEmpty(returnUrl) && Request.UrlReferrer != null)
returnUrl = Server.UrlEncode(Request.UrlReferrer.PathAndQuery);
if (Url.IsLocalUrl(returnUrl) && !string.IsNullOrEmpty(returnUrl))
_returnUrl = returnUrl;
//callback function
_redirectUrl = Url.Action("AuthorizationCodeCallback", "ExternalLogin", null, Request.Url.Scheme);
Dictionary<string, string> authorizeArgs = null;
authorizeArgs = new Dictionary<string, string>
{
{"client_id", "0123456789"}
,{"response_type", "code"}
,{"scope", "read"}
,{"redirect_uri", _redirectUrl}
// optional: state
};
var content = new FormUrlEncodedContent(authorizeArgs);
var contentAsString = await content.ReadAsStringAsync();
return Redirect("http://localhost:64426/oauth/authorize?" + contentAsString);
}
In your callback-function you exchange the authorization code for an access token (plus refresh token) challenge your passive OAuthBearer-authentication Middleware and signin with the Access token as your Cookie.
public async Task<ActionResult> AuthorizationCodeCallback()
{
// received authorization code from authorization server
string[] codes = Request.Params.GetValues("code");
var authorizationCode = "";
if (codes.Length > 0)
authorizationCode = codes[0];
// exchange authorization code at authorization server for an access and refresh token
Dictionary<string, string> post = null;
post = new Dictionary<string, string>
{
{"client_id", "0123456789"}
,{"client_secret", "ClientSecret"}
,{"grant_type", "authorization_code"}
,{"code", authorizationCode}
,{"redirect_uri", _redirectUrl}
};
var client = new HttpClient();
var postContent = new FormUrlEncodedContent(post);
var response = await client.PostAsync("http://localhost:64426/token", postContent);
var content = await response.Content.ReadAsStringAsync();
// received tokens from authorization server
var json = JObject.Parse(content);
_accessToken = json["access_token"].ToString();
_authorizationScheme = json["token_type"].ToString();
_expiresIn = json["expires_in"].ToString();
if (json["refresh_token"] != null)
_refreshToken = json["refresh_token"].ToString();
//SignIn with Token, SignOut and create new identity for SignIn
Request.Headers.Add("Authorization", _authorizationScheme + " " + _accessToken);
var ctx = Request.GetOwinContext();
var authenticateResult = await ctx.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ExternalBearer);
ctx.Authentication.SignOut(DefaultAuthenticationTypes.ExternalBearer);
var applicationCookieIdentity = new ClaimsIdentity(authenticateResult.Identity.Claims, DefaultAuthenticationTypes.ApplicationCookie);
ctx.Authentication.SignIn(applicationCookieIdentity);
var ctxUser = ctx.Authentication.User;
var user = Request.RequestContext.HttpContext.User;
//redirect back to the view which required authentication
string decodedUrl = "";
if (!string.IsNullOrEmpty(_returnUrl))
decodedUrl = Server.UrlDecode(_returnUrl);
if (Url.IsLocalUrl(decodedUrl))
return Redirect(decodedUrl);
else
return RedirectToAction("Index", "Home");
}
I hope this is useful for someone who is implementing the OAuth authorization code flow in his MVC 5 application.
I used official sample MVC Implicit Client which I believe is the correct authentication flow for MVC application.
For authorization I used this getting started, especially the part about infinite loop when roles are specified [Authorize(Roles = "Foo,Bar")] and user is authenticated but doesn't own any of these.

Resources