Can't get email or public profile facebook by ASP MVC5 - facebook

I use MVC5 to get public profile user, but I just get ID and DisplayName of user. I have researched multi ways but have no result. This my setup snippet:
var options = new FacebookAuthenticationOptions
{
AppId = "xxx",
AppSecret = "xxx",
SignInAsAuthenticationType = "ExternalCookie",
Provider = new FacebookAuthenticationProvider
{
OnAuthenticated = async context =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));
}
}
};
options.Scope.Add("email");
options.Scope.Add("public_profile");
app.UseFacebookAuthentication(options);
And I call request result in action
public async Task<ActionResult> Index()
{
var userDisplayModel = new UserDisplayModel();
var authenticateResult = await AuthenticationManager.AuthenticateAsync("ExternalCookie");
if (authenticateResult != null)
{
userDisplayModel = new UserDisplayModel()
{
DisplayName = authenticateResult.Identity.Claims.FirstOrDefault(t => t.Type == ClaimTypes.Name).Value
};
}
return View(userDisplayModel);
}

Related

asp.net core webapi JWT validation using authorization filter and JWKS

I am writing an webapi using .net core 6. It need to validate user Authorization JWT token using Authorisation filter and JWKS (keys). May I have any example. I have search google and lots of example are using middleware.
I would like to use Authorisation filter + JWKS
Token header contain below
{
"alg": "RS256",
"typ": "JWT",
**"kid": "asdfb6T82zfdfdfJwc2eruee"**
}
Please any sample code, example would be excellent.
I'm not familiar with jwks and I tried with jwt token and validate the token in filter as below:
public class JwTFilter : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
var jwt = context.HttpContext.Request.Headers["Authorization"].ToString();
var validateParameters = new TokenValidationParameters()
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("1234567890123456")),
ValidateIssuer = true,
ValidIssuer = "http://localhost:5000",
ValidateAudience = true,
ValidAudience = "http://localhost:5001",
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(5)
};
var valiresult = ValidateToken(jwt, validateParameters);
if (valiresult == false)
{
context.HttpContext.Response.StatusCode = 401;
context.Result = new JsonResult(new errmessage() { statuecode = "401", err = "Auth Failed" });
}
}
private static bool ValidateToken(string token, TokenValidationParameters validationParameters)
{
var tokenHandler = new JwtSecurityTokenHandler();
try
{
tokenHandler.ValidateToken(token, validationParameters, out var validatedToken);
return true;
}
catch (Exception)
{
return false;
}
}
}
public class errmessage
{
public string statuecode { get; set; }
public string err { get; set; }
}
In startup class:
services.AddControllers(options =>
{
options.Filters.Add<JwTFilter>();
});
The ResulT:
Update:
Is it what you want?
public override void OnAuthorization(HttpActionContext context)
{
try
{
var access_token = context.Request.Headers.Authorization?.Parameter;
if (String.IsNullOrEmpty(access_token))
{
//401
context.HttpContext.Response.StatusCode = 401;
context.Result = new JsonResult(new errmessage() { statuecode = "401", err = "Auth Failed" });
}
else
{
//access the public key
var httpclient = new HttpClient();
var jwtKey = httpclient.GetStringAsync(somejwksendpoint).Result;
//chache jwks
var Ids4keys = JsonConvert.DeserializeObject<Ids4Keys>(jwtKey);
var jwk = Ids4keys.keys;
var parameters = new TokenValidationParameters
{
ValidIssuer = issUrl,
IssuerSigningKeys = jwk,
ValidateLifetime = true,
ValidAudience = apiName
};
var handler = new JwtSecurityTokenHandler();
//validate
var vali = handler.ValidateToken(access_token, parameters, out var _);
}
}
catch (Exception ex)
{
context.HttpContext.Response.StatusCode = 401;
context.Result = new JsonResult(new errmessage() { statuecode = "401", err = "Auth Failed" });
}
}
.....
public class Ids4Keys
{
public JsonWebKey[] keys { get; set; }
}

Invalid JWT bearer token on .net 6.0 web api

I am trying to implement security to my app and used Identity and JWT bearer token to authenticate but I always get the invalid token response in my swagger. Tried many solutions but I still get the same response.
[HttpPost("[action]")]
public async Task<IActionResult> Login(LoginBindingModel login)
{
IActionResult actionResult;
var user = await userManager.FindByEmailAsync(login.Email);
if (user == null)
{
actionResult = NotFound(new {errors = new[] {$"User with email {login.Email} is not found."}});
}
else if (await userManager.CheckPasswordAsync(user, login.Password))
{
if(!user.EmailConfirmed)
{
actionResult = BadRequest(new { errors = new[] { $"Email not confirmed." } });
}
else
{
var token = GenerateTokenAsync(user);
actionResult = Ok(token);
}
}
else
{
actionResult = BadRequest(new { errors = new[] { $"Password is not valid." } });
}
return actionResult;
}
private string GenerateTokenAsync(IdentityUser user)
{
IList<Claim> userClaims = new List<Claim>
{
new Claim("UserName", user.UserName),
new Claim("Email", user.Email)
};
var x = jwtOptions.SecurityKey;
return new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(
claims: userClaims,
expires: DateTime.UtcNow.AddMonths(1),
signingCredentials: new SigningCredentials(jwtOptions.SecurityKey, SecurityAlgorithms.HmacSha256)));
}
Program.cs
using ASPNetCoreMasterAPIAssignment2.Filters;
using DomainModels;
using Infrastructure.Data.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Repositories;
using Services;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddSwaggerGen(x =>
{
x.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Description = "Please insert JWT token with bearer into field",
Name = "Authorization",
Type = SecuritySchemeType.ApiKey
});
x.AddSecurityRequirement(new OpenApiSecurityRequirement {
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] { }
}
});
});
builder.Services.AddScoped<IItemService, ItemService>();
builder.Services.AddScoped<IItemRepository, ItemRepository>();
builder.Services.AddDbContext<ItemDbContext>(opt =>
{
opt.UseSqlServer(builder.Configuration.GetConnectionString("default"));
});
builder.Services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<ItemDbContext>()
.AddDefaultTokenProviders();
SecurityKey key = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(builder.Configuration["jwt:secret"]));
builder.Services.Configure<JWTOptions>(_ => _.SecurityKey = key);
builder.Services.AddAuthentication(opt =>
{
opt.DefaultAuthenticateScheme = "Bearer";
opt.DefaultChallengeScheme = "Bearer";
opt.DefaultScheme = "Bearer";
})
.AddJwtBearer(opt =>
{
opt.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false,
IssuerSigningKey = key
};
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI();
}
else
{
app.UseExceptionHandler("/error");
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"default": "Server=localhost;Database=EfCoreDb;Trusted_Connection=True;"
},
"jwt": {
"secret": "5a927360-790b-4ba5-bae1-09aa98364090"
}
}
when i add the [Authorized] attribute to a controller, i get the following error
invalid_token can be caused by a lot of cases.
Try to view the root cause by handling the OnChallenge method (in particular the content of context.AuthenticateFailure):
return builder.AddJwtBearer(options =>
{
options.Authority = issuer;
options.Audience = audience;
options.TokenValidationParameters = new TokenValidationParameters()
{
ClockSkew = new System.TimeSpan(0, 0, 30)
};
options.Events = new JwtBearerEvents()
{
OnChallenge = context =>
{
context.HandleResponse();
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
context.Response.ContentType = "application/json";
// Ensure we always have an error and error description.
if (string.IsNullOrEmpty(context.Error))
context.Error = "invalid_token";
if (string.IsNullOrEmpty(context.ErrorDescription))
context.ErrorDescription = "This request requires a valid JWT access token to be provided";
// Add some extra context for expired tokens.
if (context.AuthenticateFailure != null && context.AuthenticateFailure.GetType() == typeof(SecurityTokenExpiredException))
{
var authenticationException = context.AuthenticateFailure as SecurityTokenExpiredException;
context.Response.Headers.Add("x-token-expired", authenticationException.Expires.ToString("o"));
context.ErrorDescription = $"The token expired on {authenticationException.Expires.ToString("o")}";
}
return context.Response.WriteAsync(JsonSerializer.Serialize(new
{
error = context.Error,
error_description = context.ErrorDescription
}));
}
};
});
source: https://sandrino.dev/blog/aspnet-core-5-jwt-authorization#configuring-jwt-bearer-authentication
In my case, it was resolved by updating some nuget, specially Microsoft.IdentityModel.Tokens : https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/1792

In AspBoilerPlate - Unauthorized error when calling from Angular when Windows Authentication is On

Have already raised this before and thought I have addressed it as per what suggested on THIS and THIS but seems not!
I am using ABP template (Angular and ASP .NET CORE Application) on Full .Net Framework. I simply want to use Windows Authentication to Authenticate user.
I added [Authorize] to the Authenticate in the TokenAuthController and have finally got the HttpContext.User.Identity.Name populated but only when I call the Authenticate from the Swagger (http://localhost:21021/swagger). But I am getting Unauthorized error when calling the method from Angular (login.service.ts):
POST http://localhost:21021/api/TokenAuth/Authenticate 401 (Unauthorized)
Here is the steps I have taken so far:
Changed launchSetting.json:
"iisSettings": {
"windowsAuthentication": true,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:21021/",
"sslPort": 0
}
},
Added ExternalAuthenticationSource:
public class WindowsAuthSource : DefaultExternalAuthenticationSource<Tenant, User>, ITransientDependency
{
public override string Name
{
get { return "Windows Authentication"; }
}
public override Task<bool> TryAuthenticateAsync(string userNameOrEmailAddress, string plainPassword, Tenant tenant)
{
return Task.FromResult(true);
}
}
Added it to CoreModule:
Configuration.Modules.Zero().UserManagement.ExternalAuthenticationSources.Add<WindowsAuthSource>();
4.Adjust AuthConfigurer:
services.AddAuthentication(opt => {
opt.DefaultScheme = IISDefaults.AuthenticationScheme;
opt.DefaultAuthenticateScheme = IISDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = IISDefaults.AuthenticationScheme;
});
Adjust StartUp.cs:
services.Configure<IISOptions>(iis =>
{
iis.AuthenticationDisplayName = "WINDOWS";
iis.AutomaticAuthentication = true;
});
Changed Authenticate method in the TokenAuthController:
public async Task<AuthenticateResultModel> Authenticate([FromBody]
AuthenticateModel model)
{
//var username = WindowsIdentity.GetCurrent().Name.Split('\\').Last();
var username = HttpContext.User.Identity.Name;
model.UserNameOrEmailAddress = username;
var loginResult = await GetLoginResultAsync(
model.UserNameOrEmailAddress,
model.Password,
null
);
var accessToken = CreateAccessToken(CreateJwtClaims(loginResult.Identity));
return new AuthenticateResultModel
{
AccessToken = accessToken,
EncryptedAccessToken = GetEncrpyedAccessToken(accessToken),
ExpireInSeconds = (int)_configuration.Expiration.TotalSeconds,
UserId = loginResult.User.Id
};
}
Sending dummy username and password from login.service.ts:
authenticate(finallyCallback?: () => void): void {
finallyCallback = finallyCallback || (() => { });
//Dummy data
this.authenticateModel.userNameOrEmailAddress = "DummyUsername";
this.authenticateModel.password = "DummyPassword";
this._tokenAuthService
.authenticate(this.authenticateModel)
.finally(finallyCallback)
.subscribe((result: AuthenticateResultModel) => {
this.processAuthenticateResult(result);
});
}

Unsupported Grant Type with CustomGrantValidator with IdentityServer 3

I'm trying to set up our IdentityServer solution to accept a custom Grant Validator. Our API project is accessed by to UIs, one that uses Password authentication (which is working) and now one that will use a 3rd party authentication.
In our API I've set up IdentityServer like so:
Startup.cs
public void Configuration(IAppBuilder app)
{
var factory = new IdentityServerServiceFactory()
.UseInMemoryClients(Clients.Get())
.UseInMemoryScopes(Scopes.Get());
var userService = new IdentityUserService();
factory.UserService = new Registration<IUserService>(resolver => userService);
factory.CustomGrantValidators.Add(
new Registration<ICustomGrantValidator, MyGrantValidator>());
var options = new IdentityServerOptions
{
SiteName = "My App Name",
SigningCertificate = Certificate.Get(),
Factory = factory
};
app.Map("/identity", identityServerApp =>
{
identityServerApp.UseIdentityServer(options);
});
}
MyGrantValidator.cs:
public class MyGrantValidator : ICustomGrantValidator
{
public async Task<CustomGrantValidationResult> ValidateAsync(ValidatedTokenRequest request)
{
// For now I just want a basic response. More logic will come later.
var authResult = new AuthenticateResult(
subject: "1234", // user.AccountId.ToString(),
name: "bob" //context.UserName
);
var grantResult = new CustomGrantValidationResult
{
IsError = authResult.IsError,
Error = authResult.ErrorMessage,
ErrorDescription = authResult.ErrorMessage,
Principal = authResult.User
};
return await Task.FromResult(grantResult);
}
public string GrantType => "myGrantType";
}
In my UI, I setup a client like this:
var owinContext = HttpContext.GetOwinContext();
var token = owinContext.Authentication.User.FindFirst(c => c.Type == "myToken")?.Value;
var tokenId = owinContext.Authentication.User.FindFirst(c => c.Type == ClaimTypes.Sid)?.Value;
var client = new TokenClient(
ConfigurationManager.AppSettings["IdentityServerBaseUrl"] + "/connect/token",
"MyUser",
ConfigurationManager.AppSettings["MyClientSecret"],
AuthenticationStyle.Custom
);
var tokenResponse = client.RequestCustomGrantAsync(
"myGrantType",
"read write",
new Dictionary<string, string>
{
{ "token", token },
{ "tokenId", tokenId }
}
).Result;
return Redirect(returnUrl);
When the Request is triggered, I get: unsupported_grant_type
What am I missing?
You're using a client called "MyUser" (weird name for a client, but ok). Is that client registered as one of the in-memory clients with grant type set to "custom"?

Facebook Share in ios.Xamarin

I'm trying to implement the Facebook share functionality in my little xamarin iOS app. I've already downloaded the latest version of Facebook iOS SDK from nuget, but I don't know how to use it. Is there anyone who has done that already , so he can send me some normal info on that ?
Much appreciated before hands :)
I have implemented share for twitter and fb .
iOS version
you can share using native social services from ios and if not available use
OAuth2Authenticator to get access token then post using FB graph
public void ShareViaSocial(string serviceType, string urlToShare)
{
socialKind = serviceType == "Twitter" ? SLServiceKind.Twitter : SLServiceKind.Facebook;
if (SLComposeViewController.IsAvailable(socialKind))
{
_socialComposer = serviceType == "Twitter" ? SLComposeViewController.FromService(SLServiceType.Twitter) : SLComposeViewController.FromService(SLServiceType.Facebook);
_socialComposer.AddUrl(new Uri(urlToShare));
viewController.PresentViewController(_socialComposer, true, () =>
{
_socialComposer.CompletionHandler += (result) =>
{
Device.BeginInvokeOnMainThread(() =>
{
viewController.DismissViewController(true, null);
if (result == SLComposeViewControllerResult.Done)
{ OnShare(this, ShareStatus.Successful); }
else
{ OnShare(this, ShareStatus.NotSuccessful); }
});
};
});
}
//If user doest have fb app and no credential for social services we use fb graph
else if (socialKind == SLServiceKind.Facebook)
{
var auth = new OAuth2Authenticator(
clientId: SharedConstants.FacebookLiveClientId,
scope: SharedConstants.FacebookScopes,
authorizeUrl: new Uri(SharedConstants.FacebookAuthorizeUrl),
redirectUrl: new Uri(SharedConstants.FacebookRedirectUrl));
viewController.PresentViewController((UIViewController)auth.GetUI(), true, null);
auth.AllowCancel = true;
auth.Completed += (s, e) =>
{
//hide the webpage after completed login
viewController.DismissViewController(true, null);
// We presented the UI, so it's up to us to dimiss it on iOS.
if (e.IsAuthenticated)
{
Account fbAccount = e.Account;
Dictionary<string, string> dictionaryParameters = new Dictionary<string, string>() { { "link", urlToShare } };
var requestUrl = new Uri("https://graph.facebook.com/me/feed");
var request = new OAuth2Request(SharedConstants.requestMethodPOST, requestUrl, dictionaryParameters, fbAccount);
request.GetResponseAsync().ContinueWith(this.requestResult);
}
else { OnShare(this, ShareStatus.NotSuccessful); }
};
auth.Error += Auth_Error;
}
//If user doest have twitter app and no credential for social services we use xanarub auth for token and call twitter api for sending tweets
else
{
var auth = new OAuth1Authenticator(
SharedConstants.TwitterConsumerKey,
SharedConstants.TwitterConsumerSecret,
new Uri(SharedConstants.TwitterRequestUrl),
new Uri(SharedConstants.TwitterAuth),
new Uri(SharedConstants.TwitterAccessToken),
new Uri(SharedConstants.TwitterCallBackUrl));
auth.AllowCancel = true;
// auth.ShowUIErrors = false;
// If authorization succeeds or is canceled, .Completed will be fired.
auth.Completed += (s, e) =>
{
// We presented the UI, so it's up to us to dismiss it.
viewController.DismissViewController(true, null);
if (e.IsAuthenticated)
{
Account twitterAccount = e.Account;
Dictionary<string, string> dictionaryParameters = new Dictionary<string, string>() { { "status", urlToShare } };
var request = new OAuth1Request(SharedConstants.requestMethodPOST, new Uri("https://api.twitter.com/1.1/statuses/update.json"), dictionaryParameters, twitterAccount);
//for testing var request = new OAuth1Request("GET",new Uri("https://api.twitter.com/1.1/account/verify_credentials.json "),null, twitterAccount);
request.GetResponseAsync().ContinueWith(this.requestResult);
}
else { OnShare(this, ShareStatus.NotSuccessful); }
};
auth.Error += Auth_Error;
//auth.IsUsingNativeUI = true;
viewController.PresentViewController((UIViewController)auth.GetUI(), true, null);
}
}