Last active
August 2, 2016 11:42
-
-
Save promontis/0f05a963491f9cf352b2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Net.Http; | |
using System.Security.Claims; | |
using System.Threading.Tasks; | |
using Microsoft.Owin.Security.Facebook; | |
using Newtonsoft.Json.Linq; | |
using Thinktecture.IdentityServer.Core.Logging; | |
using Thinktecture.IdentityServer.Core.Models; | |
using Thinktecture.IdentityServer.Core.Services; | |
using Thinktecture.IdentityServer.Core.Validation; | |
namespace Shoegle.Auth.Config | |
{ | |
public class FacebookConnectGrantValidator : ICustomGrantValidator | |
{ | |
private readonly IUserService _userService; | |
private const string XmlSchemaString = "http://www.w3.org/2001/XMLSchema#string"; | |
private const string GraphApiEndpoint = "https://graph.facebook.com/me"; | |
private readonly static ILog Logger = LogProvider.GetCurrentClassLogger(); | |
private readonly IExternalClaimsFilter _externalClaimsFilter; | |
public FacebookConnectOptions Options { get; set; } | |
public FacebookConnectGrantValidator(FacebookConnectOptions options, IUserService userService, IExternalClaimsFilter externalClaimsFilter) | |
{ | |
_userService = userService; | |
_externalClaimsFilter = externalClaimsFilter; | |
Options = options; | |
} | |
private ExternalIdentity MapToExternalIdentity(IEnumerable<Claim> claims) | |
{ | |
var externalId = ExternalIdentity.FromClaims(claims); | |
if (externalId != null && _externalClaimsFilter != null) | |
{ | |
externalId.Claims = _externalClaimsFilter.Filter(externalId.Provider, externalId.Claims); | |
} | |
return externalId; | |
} | |
public async Task<CustomGrantValidationResult> ValidateAsync(ValidatedTokenRequest request) | |
{ | |
if (request.GrantType == "facebook_connect") | |
{ | |
var accessToken = request.Raw["assertion"]; | |
using (var httpClient = new HttpClient()) | |
{ | |
var graphAddress = GraphApiEndpoint + "?access_token=" + Uri.EscapeDataString(accessToken); | |
var graphResponse = await httpClient.GetAsync(graphAddress); | |
graphResponse.EnsureSuccessStatusCode(); | |
var text = await graphResponse.Content.ReadAsStringAsync(); | |
var user = JObject.Parse(text); | |
var context = new FacebookAuthenticatedContext(null, user, accessToken, ""); | |
context.Identity = new ClaimsIdentity( | |
Options.AuthenticationType, | |
ClaimsIdentity.DefaultNameClaimType, | |
ClaimsIdentity.DefaultRoleClaimType); | |
if (!string.IsNullOrEmpty(context.Id)) | |
{ | |
context.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, context.Id, XmlSchemaString, | |
Options.AuthenticationType)); | |
} | |
if (!string.IsNullOrEmpty(context.UserName)) | |
{ | |
context.Identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.UserName, | |
XmlSchemaString, Options.AuthenticationType)); | |
} | |
if (!string.IsNullOrEmpty(context.Email)) | |
{ | |
context.Identity.AddClaim(new Claim(ClaimTypes.Email, context.Email, XmlSchemaString, | |
Options.AuthenticationType)); | |
} | |
if (!string.IsNullOrEmpty(context.Name)) | |
{ | |
context.Identity.AddClaim(new Claim("urn:facebook:name", context.Name, XmlSchemaString, | |
Options.AuthenticationType)); | |
// Many Facebook accounts do not set the UserName field. Fall back to the Name field instead. | |
if (string.IsNullOrEmpty(context.UserName)) | |
{ | |
context.Identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.Name, | |
XmlSchemaString, Options.AuthenticationType)); | |
} | |
} | |
if (!string.IsNullOrEmpty(context.Link)) | |
{ | |
context.Identity.AddClaim(new Claim("urn:facebook:link", context.Link, XmlSchemaString, | |
Options.AuthenticationType)); | |
} | |
await Options.Provider.Authenticated(context); | |
var externalIdentity = MapToExternalIdentity(context.Identity.Claims); | |
if (externalIdentity == null) | |
{ | |
Logger.Error("no subject or unique identifier claims from external identity provider"); | |
} | |
Logger.InfoFormat("external user provider: {0}, provider ID: {1}", externalIdentity.Provider, | |
externalIdentity.ProviderId); | |
var signInMessage = new SignInMessage {ClientId = request.Client.ClientId}; | |
var authResult = await _userService.AuthenticateExternalAsync(externalIdentity, signInMessage); | |
var result = new CustomGrantValidationResult { Principal = authResult.User }; | |
return result; | |
//return IdentityServerPrincipal.Create(authResult.User.S, authResult.Name, "external", authResult.Provider); | |
} | |
} | |
return null; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.Owin.Security.Facebook; | |
namespace Shoegle.Auth.Config | |
{ | |
public class FacebookConnectOptions | |
{ | |
public string AppToken { get; set; } | |
public string AuthenticationType { get; set; } | |
public IFacebookAuthenticationProvider Provider { get; set; } | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Linq; | |
using System.Security.Claims; | |
using Newtonsoft.Json.Linq; | |
namespace Shoegle.Auth.Config | |
{ | |
public class FacebookIdentityNormalizer | |
{ | |
public static void Normalize(string accessToken, JObject user, ClaimsIdentity claimsIdentity) | |
{ | |
claimsIdentity.AddClaim(new Claim("urn:facebook:accesstoken", accessToken)); | |
foreach (var claim in user) | |
{ | |
var claimType = string.Format("urn:facebook:{0}", claim.Key); | |
var claimValue = claim.Value.ToString(); | |
if (!claimsIdentity.HasClaim(claimType, claimValue)) | |
{ | |
claimsIdentity.AddClaim(new Claim(claimType, claimValue)); | |
} | |
} | |
// Normalize the profile | |
var facebookId = claimsIdentity.Claims.Single(x => x.Type == "urn:facebook:id"); | |
claimsIdentity.AddClaim(new Claim("urn:shoegler:photo", | |
string.Format("https://graph.facebook.com/{0}/picture?type=large", facebookId.Value))); | |
var facebookName = claimsIdentity.Claims.Single(x => x.Type == "urn:facebook:name"); | |
claimsIdentity.AddClaim(new Claim("urn:shoegler:name", facebookName.Value)); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment