Authorization with windows authentication in ASP.NET Core 3.1 - asp.net-core

As I understand there is a way to retrieve group where user belong.
For example Admins, Users etc.
After doing that I want to transform this into claims. I cannot find how I can retrieve a user's groups.
Currently I am using my local user and not (Domain Active Directory).
Is there any solutions for that issue?
Is it a good approach or it is better to retrieve permissions for each user from the database and then operate with them?

you need to know is that AD is working on windows host only.
read Microsoft docs before you start project
add startup configuration , create login page
services
.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/Login";
});
.....
app.UseAuthentication();
app.UseAuthorization();
third your needs:
your AD domain : yourdomain.com
your ldap url example: LDAP://DC.yourdomain.com
some knowledge about ldap query string
application.json config:
"ldap": {
"path": "LDAP://DC.yourdomain.com",
"domain": "yourdomain.com",
"personFilter": "(SAMAccountName={username})",
"groupFilter": "(&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))",
//"property": "cn,displayName,mail,givenName,sn,o,title,company,department,telephoneNumber,description,userPrincipalName",
"property": "cn,displayName,mail,givenName,sn,o,title,company,department"
//"property": "cn,member,memberof,sAMAccountName,primaryGroupToken"
}
and you can use this method to check username and password
public Dictionary<string, string> UserInfo { get; private set; }
public bool IsAuthenticated(string username, string password)
{
bool result = false;
string domainAndUsername = Configuration["ldap:domain"] + #"\" + username;
DirectoryEntry entry = new DirectoryEntry(Configuration["ldap:path"], domainAndUsername, password);
try
{
Object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = Configuration["ldap:personFilter"].Replace("{username}", username);
var propList = Configuration["ldap:property"].Split(',');
search.PropertiesToLoad.AddRange(propList);
SearchResult searchResult = search.FindOne();
if (null == searchResult)
{
return false;
}
foreach (var propName in propList)
{
UserInfo.Add(propName, GetProperty(searchResult.Properties, propName));
}
DirectoryEntry obUser = new DirectoryEntry(searchResult.Path);
object obGroups = obUser.Invoke("Groups");
var groupList = new List<string>();
foreach (object ob in (IEnumerable)obGroups)
{
DirectoryEntry obGpEntry = new DirectoryEntry(ob);
groupList.Add(obGpEntry.Name);
}
UserInfo.Add("group", string.Join(",", groupList));
result = true;
}
catch (Exception ex)
{
throw new SysAuthException("Invalid Authentication", ex);
}
return result;
}
when login success you can check all user info from userInfo property
sample code for login page (add claim and login state to net core pipeline):
try
{
if (Authentication.IsAuthenticated(UserData.Username, UserData.Password))
{
var claims = new List<Claim>() {
new Claim(ClaimTypes.Name, UserData.Username)
};
foreach (var item in Authentication.UserInfo)
{
claims.Add(new Claim(item.Key, item.Value));
}
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties()
{
IsPersistent = UserData.RememberLogin
});
if (!string.IsNullOrEmpty(UserData.ReturnUrl))
return LocalRedirect(UserData.ReturnUrl);
return Redirect("/");
}
}
catch (SysAuthException ex)
{
Error = ex.InnerException.Message;
}
if you need to protect your page add #attribute [Authorize] in top of your page also you can check other claim for example roles or group with this attribute
sample code show current user info
<div>
<table class="table table-bordered table-striped">
<caption>Current User Info</caption>
#foreach (var claim in User.Claims)
{
<tr><td>#claim.Type</td><td>#claim.Value</td></tr>
}
</table>
</div>

Related

Can't access Claim by it's key after login with ASP.NET Core Identity in .NET 6 Project

I have a Blazor project in .NET 6. I am using ASP.NET Core Identity for login. After login, I have set some Claims with a custom Key. Here is the code:
Login Code:
var createdUser = await _userManager.FindByNameAsync(Input.UserName.Trim());
var LoginResult = await _signInManager.PasswordSignInAsync(Input.UserName, Input.Password, Input.RememberMe, lockoutOnFailure: false);
if (LoginResult.Succeeded)
{
var userRoles = await _userManager.GetRolesAsync(createdUser);
var claims = new List<Claim>()
{
new Claim("UserId", createdUser?.Id),
new Claim("Username", createdUser?.UserName),
new Claim("OrganizationId", createdUser?.OrganizationId)
};
if(userRoles!= null && userRoles.Count > 0)
{
foreach (var role in userRoles)
{
claims.Add(new Claim(ClaimTypes.Role, role));
}
}
var appIdentity = new ClaimsIdentity(claims);
var claimPrincpal = new ClaimsPrincipal(appIdentity);
Thread.CurrentPrincipal = claimPrincpal;
User.AddIdentity(appIdentity);
return LocalRedirect(returnUrl);
}
After Login I have a UserInfoService class and there I have some code to get the Claim value. Here is the code below:
public class UserInfoService : IUserInfoService
{
private readonly AuthenticationStateProvider _authProvider;
private readonly UserManager<ApplicationUser> _userManager;
public UserInfoService(
AuthenticationStateProvider authProvider,
UserManager<ApplicationUser> userManager)
{
_authProvider = authProvider;
_userManager = userManager;
}
public async Task<string> GetUserName()
{
var auth = await _authProvider.GetAuthenticationStateAsync();
var user = auth.User;
if (!user.Identity.IsAuthenticated)
{
return null;
}
else
{
var claimsIdentity = user.Identity as ClaimsIdentity;
var userName = claimsIdentity.FindFirst("Username")?.Value;
if (string.IsNullOrWhiteSpace(userName))
return null;
return userName;
}
}
}
And In my Program.cs file I have the below settings for identity:
builder.Services.AddIdentity<ApplicationUser, ApplicationRole>(options => options.SignIn.RequireConfirmedAccount = false)
.AddRoles<ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
But whenever I call GetUserName method to get the username from Claim, it returns null. I tried to debug the code and during debugging whenever the breakpoint comes upon this line (var claimsIdentity = user.Identity as ClaimsIdentity;), I hover the mouse and have the below information into the Claims Property, which I don't understand how to access. I don't even see the Claims Key (UserId, Username) which I set during the login.
Can anyone help to find the answer to how can I access Claims Key and Value?
var appIdentity = new ClaimsIdentity(claims);
var claimPrincpal = new ClaimsPrincipal(appIdentity);
Thread.CurrentPrincipal = claimPrincpal;
User.AddIdentity(appIdentity);
By using the above code, the new claim will be added to the HttpContext.User, it will store data while processing a single request. The collection's contents are discarded after a request is processed.
So, I suggest you can store the Claims in the AspNetUserClaims table, you could use the UserManager.AddClaimAsync() method to add the specified claim to the user, and then use the SignInManager or ApplicationDbContext to get tye user's claims. Check the following sample code (Login post method):
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
//find current user.
var user = await _userManager.FindByEmailAsync(Input.Email);
//based on user information to query the user and role policy table. Here I set the user role directly.
var userrole = "User";
if (user.UserName.Contains("aa"))
{
userrole = "Admin";
}
//get the current user claims principal
var claimsPrincipal = await _signInManager.CreateUserPrincipalAsync(user);
//get the current user's claims.
var claimresult = claimsPrincipal.Claims.ToList();
//it it doesn't contains the Role claims, add a role claims
if (!claimresult.Any(c => c.Type == ClaimTypes.Role))
{
//add claims to current user.
await _userManager.AddClaimAsync(user, new Claim(ClaimTypes.Role, userrole));
}
//refresh the Login
await _signInManager.RefreshSignInAsync(user);
_logger.LogInformation("User logged in.");
return LocalRedirect(returnUrl);
}
Based on the answer from the Lik Now I have added the below code in my login page and it's working fine.
var claims = new List<Claim>()
{
new Claim("UserId", createdUser?.Id),
new Claim("UserName", createdUser?.UserName),
new Claim("OrganizationId", createdUser?.OrganizationId),
new Claim("OrganizationName", OrganizationName)
};
var userRoles = await _userManager.GetRolesAsync(createdUser);
if (userRoles != null && userRoles.Count > 0)
{
foreach (var role in userRoles)
{
claims.Add(new Claim(ClaimTypes.Role, role));
}
}
var claimsPrincipal = await _signInManager.CreateUserPrincipalAsync(createdUser);
var claimResult = claimsPrincipal.Claims.ToList();
if (claims != null && claims.Count > 0)
{
await _userManager.AddClaimsAsync(createdUser, claims.ToList());
}
await _signInManager.RefreshSignInAsync(createdUser);

itfoxtex saml mvccore, attribute replace NameID

I cannot figure out how to get an attribute from the the saml response in place of the NameID value.
My IDP team is returning the value I need in an attribute rather than in NameID(which they wont budge on).
Thanks for any help!
I am running MVC Core. I have everything setup and running for NameID from the example 'TestWebAppCore' for ITfoxtec.Identity.Saml2.
I am trying to get this value in place of NameID for the session username:
saml:AttributeStatement>
<saml:Attribute Name="valueName"
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"
>
<saml:AttributeValue>IDValue</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
[Route("AssertionConsumerService")]
public async Task<IActionResult> AssertionConsumerService()
{
var binding = new Saml2PostBinding();
var saml2AuthnResponse = new Saml2AuthnResponse(config);
binding.ReadSamlResponse(Request.ToGenericHttpRequest(), saml2AuthnResponse);
if (saml2AuthnResponse.Status != Saml2StatusCodes.Success) {
throw new AuthenticationException($"SAML Response status: {saml2AuthnResponse.Status}");
}
binding.Unbind(Request.ToGenericHttpRequest(),
saml2AuthnResponse);
try {
await saml2AuthnResponse.CreateSession(HttpContext,
claimsTransform: (claimsPrincipal) =>
ClaimsTransform.Transform(claimsPrincipal));
}
catch (Exception ex) {
log.writeLog(ex.Message.ToString());
}
var relayStateQuery = binding.GetRelayStateQuery();
var returnUrl = relayStateQuery.ContainsKey(relayStateReturnUrl)
? relayStateQuery[relayStateReturnUrl] : Url.Content("~/");
return Redirect(returnUrl);
}
It is probably not possible to logout without the NameID but you can login without.
In .NET the NameID is translated into the ClaimTypes.NameIdentifier claim. The users claims is handled in the ClaimsTransform.CreateClaimsPrincipal method.
You can either translate the incoming custom claim "valueName" to a ClaimTypes.NameIdentifier claim:
private static ClaimsPrincipal CreateClaimsPrincipal(ClaimsPrincipal incomingPrincipal)
{
var claims = new List<Claim>();
claims.AddRange(GetSaml2LogoutClaims(incomingPrincipal));
claims.Add(new Claim(ClaimTypes.NameIdentifier, GetClaimValue(incomingPrincipal, "valueName")));
return new ClaimsPrincipal(new ClaimsIdentity(claims, incomingPrincipal.Identity.AuthenticationType, ClaimTypes.NameIdentifier, ClaimTypes.Role)
{
BootstrapContext = ((ClaimsIdentity)incomingPrincipal.Identity).BootstrapContext
});
}
Or change the identity claim in the ClaimsIdentity to the incoming custom claim "valueName":
private static ClaimsPrincipal CreateClaimsPrincipal(ClaimsPrincipal incomingPrincipal)
{
var claims = new List<Claim>();
// All claims
claims.AddRange(incomingPrincipal.Claims);
return new ClaimsPrincipal(new ClaimsIdentity(claims, incomingPrincipal.Identity.AuthenticationType, "valueName", ClaimTypes.Role)
{
BootstrapContext = ((ClaimsIdentity)incomingPrincipal.Identity).BootstrapContext
});
}

Basic Authentication Middleware with OWIN and ASP.NET WEB API

I created an ASP.NET WEB API 2.2 project. I used the Windows Identity Foundation based template for individual accounts available in visual studio see it here.
The web client (written in angularJS) uses OAUTH implementation with web browser cookies to store the token and the refresh token. We benefit from the helpful UserManager and RoleManager classes for managing users and their roles.
Everything works fine with OAUTH and the web browser client.
However, for some retro-compatibility concerns with desktop based clients I also need to support Basic authentication. Ideally, I would like the [Authorize], [Authorize(Role = "administrators")] etc. attributes to work with both OAUTH and Basic authentication scheme.
Thus, following the code from LeastPrivilege I created an OWIN BasicAuthenticationMiddleware that inherits from AuthenticationMiddleware.
I came to the following implementation. For the BasicAuthenticationMiddleWare only the Handler has changed compared to the Leastprivilege's code. Actually we use ClaimsIdentity rather than a series of Claim.
class BasicAuthenticationHandler: AuthenticationHandler<BasicAuthenticationOptions>
{
private readonly string _challenge;
public BasicAuthenticationHandler(BasicAuthenticationOptions options)
{
_challenge = "Basic realm=" + options.Realm;
}
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
{
var authzValue = Request.Headers.Get("Authorization");
if (string.IsNullOrEmpty(authzValue) || !authzValue.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase))
{
return null;
}
var token = authzValue.Substring("Basic ".Length).Trim();
var claimsIdentity = await TryGetPrincipalFromBasicCredentials(token, Options.CredentialValidationFunction);
if (claimsIdentity == null)
{
return null;
}
else
{
Request.User = new ClaimsPrincipal(claimsIdentity);
return new AuthenticationTicket(claimsIdentity, new AuthenticationProperties());
}
}
protected override Task ApplyResponseChallengeAsync()
{
if (Response.StatusCode == 401)
{
var challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode);
if (challenge != null)
{
Response.Headers.AppendValues("WWW-Authenticate", _challenge);
}
}
return Task.FromResult<object>(null);
}
async Task<ClaimsIdentity> TryGetPrincipalFromBasicCredentials(string credentials,
BasicAuthenticationMiddleware.CredentialValidationFunction validate)
{
string pair;
try
{
pair = Encoding.UTF8.GetString(
Convert.FromBase64String(credentials));
}
catch (FormatException)
{
return null;
}
catch (ArgumentException)
{
return null;
}
var ix = pair.IndexOf(':');
if (ix == -1)
{
return null;
}
var username = pair.Substring(0, ix);
var pw = pair.Substring(ix + 1);
return await validate(username, pw);
}
Then in Startup.Auth I declare the following delegate for validating authentication (simply checks if the user exists and if the password is right and generates the associated ClaimsIdentity)
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(DbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
Func<string, string, Task<ClaimsIdentity>> validationCallback = (string userName, string password) =>
{
using (DbContext dbContext = new DbContext())
using(UserStore<ApplicationUser> userStore = new UserStore<ApplicationUser>(dbContext))
using(ApplicationUserManager userManager = new ApplicationUserManager(userStore))
{
var user = userManager.FindByName(userName);
if (user == null)
{
return null;
}
bool ok = userManager.CheckPassword(user, password);
if (!ok)
{
return null;
}
ClaimsIdentity claimsIdentity = userManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
return Task.FromResult(claimsIdentity);
}
};
var basicAuthOptions = new BasicAuthenticationOptions("KMailWebManager", new BasicAuthenticationMiddleware.CredentialValidationFunction(validationCallback));
app.UseBasicAuthentication(basicAuthOptions);
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
//If the AccessTokenExpireTimeSpan is changed, also change the ExpiresUtc in the RefreshTokenProvider.cs.
AccessTokenExpireTimeSpan = TimeSpan.FromHours(2),
AllowInsecureHttp = true,
RefreshTokenProvider = new RefreshTokenProvider()
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
}
However, even with settings the Request.User in Handler's AuthenticationAsyncCore method the [Authorize] attribute does not work as expected: responding with error 401 unauthorized every time I try to use the Basic Authentication scheme.
Any idea on what is going wrong?
I found out the culprit, in the WebApiConfig.cs file the 'individual user' template inserted the following lines.
//// Web API configuration and services
//// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
Thus we also have to register our BasicAuthenticationMiddleware
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
config.Filters.Add(new HostAuthenticationFilter(BasicAuthenticationOptions.BasicAuthenticationType));
where BasicAuthenticationType is the constant string "Basic" that is passed to the base constructor of BasicAuthenticationOptions
public class BasicAuthenticationOptions : AuthenticationOptions
{
public const string BasicAuthenticationType = "Basic";
public BasicAuthenticationMiddleware.CredentialValidationFunction CredentialValidationFunction { get; private set; }
public BasicAuthenticationOptions( BasicAuthenticationMiddleware.CredentialValidationFunction validationFunction)
: base(BasicAuthenticationType)
{
CredentialValidationFunction = validationFunction;
}
}

MVC4 how do I set a cookie and then redirect to an action

Hi I am trying to get a users role and set it to a cookie in my application
I have the following code which works
public ActionResult Index()
{
var user = User.Identity.Name; // set by 3rd party central login in manager
// key to check that we are in our environment with 3rd party login set up
if (ConfigurationManager.AppSettings["IsNGDC"] == "true")
{
// ActiveKey login
if (user.Contains("uid="))
{
var endIndex = user.IndexOf(",ou");
var userEmail = user.Substring(4, endIndex - 4);
user = userEmail;
}
SetAuthenticationCookie(user);
}
// view model is not needed I could just pass in a string
var viewModel = new SiteminderViewModel { Username = user };
if (ModelState.IsValid)
{
this.AssignRoles(viewModel);
return this.View();
}
return View(viewModel);
}
I need to change this because I am using a dynamic Navigation bar that shows different Items depending on the users role and it does not show the correct Nav bar until the user refreshes the page. I think this is because the view uses the cookie and the view is being rendered in the same action that sets the cookie.
I want to split this into 2 actions in my controller as follows
public void LogIn()
{
var user = User.Identity.Name; // set by 3rd party central login in manager
// key to check that we are in our environment with 3rd party login set up
if (ConfigurationManager.AppSettings["IsNGDC"] == "true")
{
// ActiveKey login
if (user.Contains("uid="))
{
var endIndex = user.IndexOf(",ou");
var userEmail = user.Substring(4, endIndex - 4);
user = userEmail;
}
SetAuthenticationCookie(user);
}
// view model is not needed I could just pass in a string
var viewModel = new SiteminderViewModel { Username = user };
this.AssignRoles(viewModel);
// default URL in Index action for this controller
this.Response.Redirect(FormsAuthentication.DefaultUrl, false);
}
public ActionResult Index()
{
ViewBag.Message = "Home App Description here";
return this.View();
}
When I try this it looks like the Cookie hasn't been set. Unfortunately I can only test this code on a replication of our Production Environment because of the 3rd party login so I have limited debug info. As far as I can tell the problem seems to be with how I am redirecting.
I have provided the methods I use cor creating the cookie and assigning the roles bellow.
Additional Info
private void SetAuthenticationCookie(string username)
{
var tkt = new FormsAuthenticationTicket(1, username, DateTime.UtcNow, DateTime.UtcNow.AddMinutes(20), true, string.Empty);
var encryptedTkt = FormsAuthentication.Encrypt(tkt);
var formsCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTkt);
this.Response.Cookies.Add(formsCookie);
}
private void AssignRoles(SiteminderViewModel viewModel)
{
var authCookie = System.Web.HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
var ticket = authCookie != null ? FormsAuthentication.Decrypt(authCookie.Value) : new FormsAuthenticationTicket(1, viewModel.Username, DateTime.UtcNow, DateTime.UtcNow.AddMinutes(20), true, string.Empty);
var user = this.userRepository.GetUser(viewModel.Username);
if (user != null)
{
var principleProperties = new PrincipleProperties(ticket.UserData)
{
UserName = user.Email,
UserRole = user.UserGroup.Role.Name.Replace(" ", string.Empty),
ContextId = contextRepository.GetContextByDataOwnerGroupId(user.UserGroupId)
};
if (user.DeletedIndicator)
{
principleProperties.UserRole = string.Empty;
}
this.SetPrinciple(ticket, principleProperties);
}
}
private FormsAuthenticationTicket SetPrinciple(FormsAuthenticationTicket ticket, PrincipleProperties properties)
{
var newticket = new FormsAuthenticationTicket(
ticket.Version,
ticket.Name,
ticket.IssueDate,
ticket.Expiration,
ticket.IsPersistent,
properties.Serialize(),
ticket.CookiePath);
var encryptedTkt = FormsAuthentication.Encrypt(newticket);
var formsCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTkt);
this.Response.Cookies.Set(formsCookie);
var referenceDataIdentity = new ReferenceDataIdentity(ticket);
var principle = new ReferenceDataPrinciple(referenceDataIdentity, properties);
Thread.CurrentPrincipal = principle;
return newticket;
}
The solution to this was that the cookie wasn't being added to the browser because I was redirecting before the cookie reached the client side the solution was to have the Login Action return a blank view and then from inside the view redirect to the Index action the final version of my code ended up as follows NOTE: Login changed to AuthenticateUser
public ActionResult Index()
{
var authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
var ticket = FormsAuthentication.Decrypt(authCookie.Value);
if (ticket != null && ticket.UserData != string.Empty)
{
return this.View();
}
}
return RedirectToAction("AuthenticateUser");
}
public ActionResult AuthenticateUser()
{
// set by Site minder
var user = User.Identity.Name;
// ActiveKey login
if (user.Contains("uid="))
{
var endIndex = user.IndexOf(",ou");
var userEmail = user.Substring(4, endIndex - 4);
user = userEmail;
}
SetAuthenticationCookie(user);
var viewModel = new SiteminderViewModel { Username = user };
this.AssignRoles(viewModel);
return this.View();
}
and the view is. There is no HTML to display so the redirect is not noticeable.
#{
ViewBag.Title = "AuthenticateUser";
Layout = null;
Response.Redirect( Url.Action("Index", "Home"), false);
}
This code is checking that there is a cookie and that the user data isn't empty, if theses checks pass it shows the user the home page. Otherwise it redirects to the authentication action that gets the email address that was set in the browser by our 3rd party central login software and gets the users details from the users details. If the user is not in our user table they are given basic access rights.

LinkedIn full profile details using DotNetOpenAuth in MVC4

My MVC4 application allows login using LinkedIn account. I want to pull all details that are avaible from linkedIn of the logged in User. Currently i have done the following.
In My AuthConfig.cs,
Dictionary<string, object> linkedInExtraData = new Dictionary<string, object>();
linkedInExtraData.Add("Icon", "../Images/linkedIn.png");
OAuthWebSecurity.RegisterClient(
client: new App_Start.LinkedInCustomClient("xxxxxxxxxxxx", "yyyyyyyyyyyyyyy"),
displayName: "LinkedIn",
extraData: linkedInExtraData);
In linkedInCustomClient.cs , from LinkedIn Developer Kit
public class LinkedInCustomClient : OAuthClient
{
private static XDocument LoadXDocumentFromStream(Stream stream)
{
var settings = new XmlReaderSettings
{
MaxCharactersInDocument = 65536L
};
return XDocument.Load(XmlReader.Create(stream, settings));
}
/// Describes the OAuth service provider endpoints for LinkedIn.
private static readonly ServiceProviderDescription LinkedInServiceDescription =
new ServiceProviderDescription
{
AccessTokenEndpoint =
new MessageReceivingEndpoint("https://api.linkedin.com/uas/oauth/accessToken",
HttpDeliveryMethods.PostRequest),
RequestTokenEndpoint =
new MessageReceivingEndpoint("https://api.linkedin.com/uas/oauth/requestToken?scope=r_fullprofile",
HttpDeliveryMethods.PostRequest),
UserAuthorizationEndpoint =
new MessageReceivingEndpoint("https://www.linkedin.com/uas/oauth/authorize",
HttpDeliveryMethods.PostRequest),
TamperProtectionElements =
new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
ProtocolVersion = ProtocolVersion.V10a
};
public LinkedInCustomClient(string consumerKey, string consumerSecret) :
base("linkedIn", LinkedInServiceDescription, consumerKey, consumerSecret) { }
/// Check if authentication succeeded after user is redirected back from the service provider.
/// The response token returned from service provider authentication result.
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
Justification = "We don't care if the request fails.")]
protected override AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response)
{
// See here for Field Selectors API http://developer.linkedin.com/docs/DOC-1014
const string profileRequestUrl =
"https://api.linkedin.com/v1/people/~:(id,first-name,last-name,interests,headline,industry,summary,email-address,location:(name),picture-url,positions,associations,languages,honors,educations,date-of-birth,primary-twitter-account,three-current-positions,three-past-positions,group-memberships,specialties,skills)";
string accessToken = response.AccessToken;
string tokenSecret = (response as ITokenSecretContainingMessage).TokenSecret;
string Verifier = response.ExtraData.Values.First();
var profileEndpoint =
new MessageReceivingEndpoint(profileRequestUrl, HttpDeliveryMethods.GetRequest);
HttpWebRequest request =
WebWorker.PrepareAuthorizedRequest(profileEndpoint, accessToken);
try
{
using (WebResponse profileResponse = request.GetResponse())
{
using (Stream responseStream = profileResponse.GetResponseStream())
{
XDocument document = LoadXDocumentFromStream(responseStream);
return new AuthenticationResult(
isSuccessful: true,
provider: ProviderName,
providerUserId: userId,
userName: userName,
extraData: extraData);
}
}
}
catch (Exception exception)
{
return new AuthenticationResult(exception);
}
}
}
In my controller,
AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
if (!result.IsSuccessful)
{
return RedirectToAction("ExternalLoginFailure");
}
I need to get the following details in my controller as authentication result.
(id,first-name,last-name,interests,headline,industry,summary,email-address,location:(name),picture-url,positions,associations,languages,honors,educations,date-of-birth,primary-twitter-account,three-current-positions,three-past-positions,group-memberships,specialties,skills)
The response of your request from LinkedIn will be a xml file. The format and fields are mentioned in LinkedIn Profile Fields
For getting email field, you need to modify your request token url as
RequestTokenEndpoint = new MessageReceivingEndpoint("https://api.linkedin.com/uas/oauth/requestToken?scope=r_fullprofile+r_emailaddress",
HttpDeliveryMethods.PostRequest),
You can get the fields as required in the following code
XDocument document = LoadXDocumentFromStream(responseStream);
Eg : For getting the first name field from the xml file,
var firstName = document.Root.Element("first-name").Value;
Fields like languages, positions, skills etc will be returned as structured objects as part of the profile.
Eg : Language field.
var Lang = document.Root.Element("languages");
var languages = new List<string>();
if (Lang != null)
{
foreach (var l in Lang.Elements())
{
if (l.Element("language") != null && l.Element("language").Element("name") != null)
{
languages.Add(l.Element("language").Element("name").Value);
}
}
}
Then you can add fields to "extraData" which can be accessed in the controller.
extraData.Add("firstName", firstName);
extraData.Add("languages", lang);