Trying to create custom ServiceHostFactory and see the following error logged in the eventviewer.
WebHost failed to process a request.
Sender Information: System.ServiceModel.ServiceHostingEnvironment+HostingManager/38902774
Exception: System.ServiceModel.ServiceActivationException: The service '/services/clientservices.svc' cannot be activated due to an exception during compilation. The exception message is: The security token manager cannot create a token authenticator for requirement 'System.ServiceModel.Security.Tokens.RecipientServiceModelSecurityTokenRequirement:
PropertyName: http://schemas.microsoft.com/ws/2006/05/identitymodel/securitytokenrequirement/KeyType
PropertyValue: SymmetricKey
PropertyName: http://schemas.microsoft.com/ws/2006/05/identitymodel/securitytokenrequirement/KeyUsage
PropertyValue: Signature
PropertyName: http://schemas.microsoft.com/ws/2006/05/identitymodel/securitytokenrequirement/RequireCryptographicToken
PropertyValue: True
PropertyName: http://schemas.microsoft.com/ws/2006/05/identitymodel/securitytokenrequirement/KeySize
PropertyValue: 0
PropertyName: http://schemas.microsoft.com/ws/2006/05/identitymodel/securitytokenrequirement/IsOptionalTokenProperty
PropertyValue: False
PropertyName: http://schemas.microsoft.com/ws/2006/05/servicemodel/securitytokenrequirement/SupportSecurityContextCancellation
PropertyValue: False
PropertyName: http://schemas.microsoft.com/ws/2006/05/servicemodel/securitytokenrequirement/IsInitiator
PropertyValue: False
PropertyName: http://schemas.microsoft.com/ws/2006/05/servicemodel/securitytokenrequirement/SecurityBindingElement
PropertyValue: System.ServiceModel.Channels.SymmetricSecurityBindingElement:
DefaultAlgorithmSuite: Basic256
IncludeTimestamp: True
KeyEntropyMode: CombinedEntropy
MessageSecurityVersion: WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11
SecurityHeaderLayout: Strict
ProtectTokens: False
EndpointSupportingTokenParameters:
Endorsing[0]
System.ServiceModel.Security.Tokens.IssuedSecurityTokenParameters:
InclusionMode: AlwaysToRecipient
ReferenceStyle: Internal
RequireDerivedKeys: True
TokenType: samlTokenType
KeyType: SymmetricKey
KeySize: 0
IssuerAddress: https://sirona-locl-use.accesscontrol.windows.net/v2/wstrust/13/certificate
IssuerMetadataAddress: null
DefaultMessgeSecurityVersion: null
UseStrTransform: False
IssuerBinding: null
ClaimTypeRequirements: none
No signed tokens.
No signed encrypted tokens.
No signed endorsing tokens.
OptionalEndpointSupportingTokenParameters:
No endorsing tokens.
No signed tokens.
No signed encrypted tokens.
No signed endorsing tokens.
OperationSupportingTokenParameters: none
OptionalOperationSupportingTokenParameters: none
MessageProtectionOrder: SignBeforeEncryptAndEncryptSignature
RequireSignatureConfirmation: True
ProtectionTokenParameters: System.ServiceModel.Security.Tokens.X509SecurityTokenParameters:
InclusionMode: Never
ReferenceStyle: Internal
RequireDerivedKeys: True
X509ReferenceStyle: Thumbprint
....
Here is the code that i am using create servicehostfactory
public class WSTrustServiceHostFactory : ServiceHostFactory
{
public static Binding CreateIssuedTokenForCertificateBinding(string acsCertificateEndpoint)
{
//http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0
BindingElementCollection bec = new BindingElementCollection();
bec.Add(SecurityBindingElement.
CreateIssuedTokenForCertificateBindingElement(
new IssuedSecurityTokenParameters("samlTokenType", new EndpointAddress(acsCertificateEndpoint))));
bec.Add(new TextMessageEncodingBindingElement());
bec.Add(new HttpTransportBindingElement());
return new CustomBinding(bec);
}
protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
if (serviceType == null)
throw new ArgumentNullException("serviceType cannot be null");
if (baseAddresses.Count() == 0)
throw new ArgumentException("baseAddresses must have at least 1 member.");
string acsCertificateEndpoint = "https://acs url ...."
WSFederationHttpSecurityMode securityMode = WSFederationHttpSecurityMode.TransportWithMessageCredential;
if ( debugging )
{
securityMode = WSFederationHttpSecurityMode.Message;
}
ServiceHost serviceHost = new ServiceHost(serviceType, baseAddresses);
//IssuedTokenWSTrustBinding issuedTokenWSTrustBinding = new IssuedTokenWSTrustBinding(
// new CertificateWSTrustBinding(securityMode),
// new EndpointAddress(acsCertificateEndpoint));
System.IdentityModel.Configuration.IdentityConfiguration serviceConfiguration =
new System.IdentityModel.Configuration.IdentityConfiguration();
serviceHost.Credentials.ServiceCertificate.Certificate = // fetch acs decryption certificate;
acsSigningCertificate = //fetch acs signing certificate.
ConfigurationBasedIssuerNameRegistry issuerNameRegistry = new ConfigurationBasedIssuerNameRegistry();
issuerNameRegistry.AddTrustedIssuer(acsSigningCertificate.Thumbprint, acsSigningCertificate.SubjectName.Name);
serviceConfiguration.IssuerNameRegistry = issuerNameRegistry;
serviceConfiguration.AudienceRestriction.AudienceMode = System.IdentityModel.Selectors.AudienceUriMode.Always;
serviceConfiguration.CertificateValidationMode = X509CertificateValidationMode.None;
serviceConfiguration.SecurityTokenHandlers.AddOrReplace(new Saml2SecurityTokenHandler());
// wif 3.5 //serviceHost.AddServiceEndpoint(serviceType.GetInterfaces()[0], issuedTokenWSTrustBinding, String.Empty);
serviceHost.AddServiceEndpoint(serviceType.GetInterfaces()[0], CreateIssuedTokenForCertificateBinding(acsCertificateEndpoint), String.Empty);
//var creds = serviceHost.Description.Behaviors.Find<ServiceCredentials>();
// creds.UseIdentityConfiguration = true;
//creds.IdentityConfiguration = serviceConfiguration;
serviceHost.Credentials.UseIdentityConfiguration = true;
serviceHost.Credentials.IdentityConfiguration = serviceConfiguration;
// <--wif 3.5 FederatedServiceCredentials.ConfigureServiceHost(serviceHost, serviceConfiguration); -->
if (RegionConfiguration.GetSetting<bool>(Settings.CLIENTSERVICES_INCLUDE_EXCEPTION_DETAILS))
{
if (serviceHost.Description.Behaviors.Find<ServiceDebugBehavior>() == null)
{
serviceHost.Description.Behaviors.Add(new ServiceDebugBehavior());
}
serviceHost.Description.Behaviors.Find<ServiceDebugBehavior>().IncludeExceptionDetailInFaults = true;
}
return serviceHost;
}
}
any ideas whats going on here ?
Maybe have a look here as a "template"
https://github.com/thinktecture/Thinktecture.IdentityServer.v2/blob/master/src/Libraries/Thinktecture.IdentityServer.Protocols/WSTrust/TokenServiceHostFactory.cs
Related
I currently have webservice calls that create a proxy interface for a URL. I have a requirement to update the application to accept Oauth 2.0. Is it possible to use Oauth 2.0 with WCF Webservice calls?
This is my proxy interface initialization. I use it just like a regular class initialization.
var client = ServiceClient.CreateProxyInterface<MyWebServiceClass>(WebServiceUrl);
inside the proxy interface I do some authorization checks and create an instance of the requested object and return it back to the client
public static TInterface CreateProxyInterface<TInterface>(string ServiceUrl) where TInterface : class
{
var UseClientCertificate = true;
if (ServiceClient.IsUnsecuredHttpService(ServiceUrl, UseClientCertificate))
ServiceUrl = new UriBuilder(ServiceUrl)
{
Scheme = Uri.UriSchemeHttps,
Port = -1
}.Uri.ToString();
var key = TInterface.ToString() + ServiceUrl + UseClientCertificate.ToString();
ChannelFactory myChannelFactory = ServiceClient.FactoryCache[key];
proxy = ((ChannelFactory<TInterface>) mlifChannelFactory1.Factory).CreateChannel();
return proxyInterface;
}
the client can then call a method within that class
var address = client.GetAddress(personId);
On the server side, you can customize a class to inherit ServiceAuthorizationManager, and then override the CheckAccessCore method in ServiceAuthorizationManager to implement it.
Below is an example I found from previous answers:OAuth and WCF SOAP service. After my attempts, his example is effective, so I think it should help you.
public class OAuthAuthorizationManager : ServiceAuthorizationManager
{
protected override bool CheckAccessCore(OperationContext operationContext)
{
// Extract the action URI from the OperationContext. Match this against the claims
// in the AuthorizationContext.
string action = operationContext.RequestContext.RequestMessage.Headers.Action;
try
{
//get the message
var message = operationContext.RequestContext.RequestMessage;
//get the http headers
var httpHeaders = ((System.ServiceModel.Channels.HttpRequestMessageProperty)message.Properties.Values.ElementAt(message.Properties.Keys.ToList().IndexOf("httpRequest"))).Headers;
//get authorization header
var authHeader = httpHeaders.GetValues("Authorization");
if (authHeader != null)
{
var parts = authHeader[0].Split(' ');
if (parts[0] == "Bearer")
{
var tokenClaims = ValidateJwt(parts[1]);
foreach (System.Security.Claims.Claim c in tokenClaims.Where(c => c.Type == "http://www.contoso.com/claims/allowedoperation"))
{
var authorized = true;
//other claims authorization logic etc....
if(authorized)
{
return true;
}
}
}
}
return false;
}
catch (Exception)
{
throw;
}
}
private static IEnumerable<System.Security.Claims.Claim> ValidateJwt(string jwt)
{
var handler = new JwtSecurityTokenHandler();
var validationParameters = new TokenValidationParameters()
{
ValidAudience = "urn://your.audience",
IssuerSigningKey = new InMemorySymmetricSecurityKey(Convert.FromBase64String("base64encoded symmetric key")),
ValidIssuer = "urn://your.issuer",
CertificateValidator = X509CertificateValidator.None,
RequireExpirationTime = true
};
try
{
SecurityToken validatedToken;
var principal = handler.ValidateToken(jwt, validationParameters, out validatedToken);
return principal.Claims;
}
catch (Exception e)
{
return new List<System.Security.Claims.Claim>();
}
}
}
My app requirements is to authenticate using client credentials AND another code (hash).
I followed this link to create and use custom IExtensionGrantValidator.
I manged to invoke the custom IExtensionGrantValidator with approved grant, but client always gets invalid_grant error.
For some reason the set operation ofd Result (property of ExtensionGrantValidationContext) always fails (overriding the Error value returns the overrided value to client).
This is CustomGrantValidator Code:
public class CustomGrantValidator : IExtensionGrantValidator
{
public string GrantType => "grant-name";
public Task ValidateAsync(ExtensionGrantValidationContext context)
{
var hash = context.Request.Raw["hash"]; //extract hash from request
var result = string.IsNullOrEmpty(hash) ?
new GrantValidationResult(TokenRequestErrors.InvalidRequest) :
new GrantValidationResult(hash, GrantType);
context.Result = result
}
}
Startup.cs contains this line:
services.AddTransient<IExtensionGrantValidator, CustomGrantValidator>();
And finally client's code:
var httpClient = new HttpClient() { BaseAddress = new Uri("http://localhost:5000") };
var disco = await httpClient.GetDiscoveryDocumentAsync("http://localhost:5000");
var cReq = await httpClient.RequestTokenAsync(new TokenRequest
{
GrantType = "grant-name",
Address = disco.TokenEndpoint,
ClientId = clientId,// client Id taken from appsetting.json
ClientSecret = clientSecret, //client secret taken from appsetting.json
Parameters = new Dictionary<string, string> { { "hash", hash } }
});
if (cReq.IsError)
//always getting 'invalid_grant' error
throw InvalidOperationException($"{cReq.Error}: {cReq.ErrorDescription}");
The below codes works on my environment :
public async Task ValidateAsync(ExtensionGrantValidationContext context)
{
var hash = context.Request.Raw["hash"]; //extract hash from request
var result = string.IsNullOrEmpty(hash) ?
new GrantValidationResult(TokenRequestErrors.InvalidRequest) :
new GrantValidationResult(hash, GrantType);
context.Result = result;
return;
}
Don't forget to register the client to allow the custom grant :
return new List<Client>
{
new Client
{
ClientId = "client",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = { "grant-name" },
// secret for authentication
ClientSecrets =
{
new Secret("secret".Sha256())
},
// scopes that client has access to
AllowedScopes = { "api1" }
}
};
I got the same issue and found the answer from #Sarah Lissachell, turn out that I need to implement the IProfileService. This interface has a method called IsActiveAsync. If you don't implement this method, the answer of ValidateAsync will always be false.
public class IdentityProfileService : IProfileService
{
//This method comes second
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
//IsActiveAsync turns out to be true
//Here you add the claims that you want in the access token
var claims = new List<Claim>();
claims.Add(new Claim("ThisIsNotAGoodClaim", "MyCrapClaim"));
context.IssuedClaims = claims;
}
//This method comes first
public async Task IsActiveAsync(IsActiveContext context)
{
bool isActive = false;
/*
Implement some code to determine that the user is actually active
and set isActive to true
*/
context.IsActive = isActive;
}
}
Then you have to add this implementation in your startup page.
public void ConfigureServices(IServiceCollection services)
{
// Some other code
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddAspNetIdentity<Users>()
.AddInMemoryApiResources(config.GetApiResources())
.AddExtensionGrantValidator<CustomGrantValidator>()
.AddProfileService<IdentityProfileService>();
// More code
}
How can one use HttpClient with a pipeline of multiple proxies?
A single proxy can be handled via HttpClientHandler:
HttpClient client1 = new HttpClient(new HttpClientHandler()
{
Proxy = new WebProxy()
{
Address = new Uri($"http://{proxyIp}:{proxyPort}"),
BypassProxyOnLocal = false,
UseDefaultCredentials = false
}
});
I want the requests to pass through multiple proxies.
I already tried subclassing DelegatingHandler like this:
public class ProxyDelegatingHandler : DelegatingHandler
{
public ProxyDelegatingHandler(string proxyIp, int proxyPort):
base(new HttpClientHandler()
{
Proxy = new WebProxy()
{
Address = new Uri($"http://{proxyIp}:{proxyPort}"),
BypassProxyOnLocal = false,
UseDefaultCredentials = false
}
})
{
}
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken);
}
}
And passing the list to factory, but it throws an exception which is probably caused by incorrect implementation of ProxyDelegatingHandler:
var handlers = new List<DelegatingHandler>();
handlers.Add(new ProxyDelegatingHandler(ip1, port2));
handlers.Add(new ProxyDelegatingHandler(ip2, port2));
HttpClient client = HttpClientFactory.Create(handlers.ToArray())
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, url);
var res = await client.SendAsync(requestMessage);
Exception:
The 'DelegatingHandler' list is invalid because the property 'InnerHandler' of 'CustomHandler' is not null. Parametername: handlers
Related Post: link
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;
}
}
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);