Identity Server 4 Auto Login After Registration - identityserver4

I want the user to be auto logged after registration to the MVC app (email verification is not needed now), I have followed this sample for IdSrv3 and edited some parts:
http://benfoster.io/blog/identity-server-post-registration-sign-in
Here is my Setup:
MVC:
public class AuthController : Controller
{
[HttpGet]
[AllowAnonymous]
public async Task LogIn()
{
var properties = new AuthenticationProperties
{
RedirectUri = Url.Action("index", "home", HttpContext.Request.GetUri().Scheme)
};
await HttpContext.Authentication.ChallengeAsync(properties);
}
[HttpGet]
[AllowAnonymous]
public IActionResult Register()
{
var returnUrl = $"http://localhost:5002/auth/login";
return Redirect($"http://localhost:5000/Account/Register?returnUrl={returnUrl}");
}
}
Identity Server 4 using ASp.Net Core Identity
[Authorize]
public class AccountController : Controller
{
//
// GET: /Account/Register
[HttpGet]
[AllowAnonymous]
public IActionResult Register(string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
return View();
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
var otac = user.GenerateOTAC(TimeSpan.FromMinutes(1));
await _userManager.UpdateAsync(user);
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation(3, "User created a new account with password.");
if (string.IsNullOrEmpty(returnUrl))
{
return RedirectToAction(nameof(HomeController.Index), "Home");
}
else
{
return Redirect(returnUrl);
}
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
}
I could not implement the OTAC handshake as the sample shows becaus there is no User Service any more in IdSrv 4.
The scenario works as follows:
1 - User clicks 'Register' in MVC app whish redirects to the IdSrv Account->Register passing the MVC Auth->login as a return URI.
2 - After the user completed the registration in IdSrv another redirect executes to return back to the MVC Auth->Login.
3 - The MVC Auth->Login creates a challenge and since the user is already signed in in the registration process using the cookie, the authentication cookie gets merged (as per the IdSrv log), and there is no login screen appears and now the user is logged in and landed to the MVC app.
Does this setup have any secutity flaw?, is there any way I can implement the OTAC handshake in IdSrv4?
thanks,

In your case maybe this could work
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}

Related

React and asp.net core authentication and authorization with google

I've been trying to get my authentication and authorization process working with google for two weeks now using react on the frontend and asp.net core on the backend.
Incredible as it may seem, I can't find a guide that addresses all this development context despite being something very basic.
Anyway, the best I could get was this code below, but I don't know how instead of returning the View, return my frontend in React.
And also how to do the process in react.
I know the question may sound a little generic, but I'm really lost.
This is my controller
public IActionResult GoogleLogin()
{
string redirectUrl = Url.Action("GoogleResponse", "Account");
var properties = signInManager.ConfigureExternalAuthenticationProperties("Google", redirectUrl);
return new ChallengeResult("Google", properties);
}
[AllowAnonymous]
public async Task<IActionResult> GoogleResponse()
{
ExternalLoginInfo info = await signInManager.GetExternalLoginInfoAsync();
if (info == null)
return RedirectToAction(nameof(Login));
var result = await signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, false);
string[] userInfo = { info.Principal.FindFirst(ClaimTypes.Name).Value, info.Principal.FindFirst(ClaimTypes.Email).Value };
if (result.Succeeded)
return View(userInfo);
else
{
AppUser user = new AppUser
{
Email = info.Principal.FindFirst(ClaimTypes.Email).Value,
UserName = info.Principal.FindFirst(ClaimTypes.Email).Value
};
IdentityResult identResult = await userManager.CreateAsync(user);
if (identResult.Succeeded)
{
identResult = await userManager.AddLoginAsync(user, info);
if (identResult.Succeeded)
{
await signInManager.SignInAsync(user, false);
return View(userInfo);
}
}
return AccessDenied();
}
}`
This is my Startup
`services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<AppDbContext>()
.AddDefaultTokenProviders();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
});
services.AddAuthentication()
.AddGoogle(opts =>
{
opts.ClientId = "715561755180-h72ug8g5v4sfgcn150n8mjaq75oacmp8.apps.googleusercontent.com";
opts.ClientSecret = "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";
opts.SignInScheme = IdentityConstants.ExternalScheme;
});`
I have a regular jwt authentication with email and password works well with react,.net core and identity, but google login is my problem
I expect a guide or tips to help me move forward with the project

Getting "No authentication handler is registered for the scheme 'Windows'" error when trying Windows Authetication

I want to enable Windows Authentication for my IdentityServer hosted by IIS (in process). The doc from IdentityServer4 stated that "You trigger Windows authentication by calling ChallengeAsync on the Windows scheme" but it does say where and how. I was assuming it is in the Login function of the AccountController but it doesn't seem to work for me.
Here is the error I am getting when running my IdentityServer
But to my knowledge, I had registered the authentication for Windows scheme. Here is the ConfigurtionServices and Confiuration functions of the Startup.cs for the identityserver:
public void ConfigureServices(IServiceCollection services)
{
IdentityModelEventSource.ShowPII = true;
services.AddControllersWithViews();
var connstr = Configuration.GetConnectionString("DBConnection");
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connstr));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
var builder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
options.EmitStaticAudienceClaim = true;
})
.AddAspNetIdentity<ApplicationUser>()
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = b => b.UseSqlServer(connstr,
sql => sql.MigrationsAssembly(migrationsAssembly));
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = b => b.UseSqlServer(connstr,
sql => sql.MigrationsAssembly(migrationsAssembly));
});
// not recommended for production - you need to store your key material somewhere secure
builder.AddDeveloperSigningCredential();
//services.AddIdentityServer().AddSigningCredential(
// new X509Certificate2(Path.Combine(_environment.ContentRootPath, "certs", "IdentityServer4Auth.pfx")));
// configures IIS in-proc settings
services.Configure<IISServerOptions>(iis =>
{
iis.AuthenticationDisplayName = "Windows";
iis.AutomaticAuthentication = false;
});
// for Windows authentication
services.AddAuthentication(IISDefaults.AuthenticationScheme);
}
public void Configure(IApplicationBuilder app)
{
if (Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
// use MVC
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
// use MVC
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
As you can see the very last line in ConfigurationServices function, I registered authentication handler for Windows by calling services.AddAuthentication(IISDefaults.AuthenticationScheme).
Here is the Login function in the AccountController.cs, in which I call ChallengeWindowsAsync - I thought this is how the IdentityServer4 Doc suggested to do but I am not sure. If it is not the right way, how should I correct it?
[HttpGet]
public async Task<IActionResult> Login(string returnUrl)
{
// trigger Windows authentication by calling ChallengeAsync
await ChallengeWindowsAsync(returnUrl);
// build a model so we know what to show on the login page
var vm = await BuildLoginViewModelAsync(returnUrl);
if (vm.IsExternalLoginOnly)
{
// we only have one option for logging in and it's an external provider
return RedirectToAction("Challenge", "External", new { scheme = vm.ExternalLoginScheme, returnUrl });
}
return View(vm);
}
And here is the ChallengeWindowsAsync function
private async Task<IActionResult> ChallengeWindowsAsync(string returnUrl)
{
// see if windows auth has already been requested and succeeded
var result = await HttpContext.AuthenticateAsync("Windows");
if (result?.Principal is WindowsPrincipal wp)
{
// we will issue the external cookie and then redirect the
// user back to the external callback, in essence, treating windows
// auth the same as any other external authentication mechanism
var props = new AuthenticationProperties()
{
RedirectUri = Url.Action("Callback"),
Items =
{
{ "returnUrl", returnUrl },
{ "scheme", "Windows" },
}
};
var id = new ClaimsIdentity("Windows");
// the sid is a good sub value
id.AddClaim(new Claim(JwtClaimTypes.Subject, wp.FindFirst(ClaimTypes.PrimarySid).Value));
// the account name is the closest we have to a display name
id.AddClaim(new Claim(JwtClaimTypes.Name, wp.Identity.Name));
// add the groups as claims -- be careful if the number of groups is too large
var wi = wp.Identity as WindowsIdentity;
// translate group SIDs to display names
var groups = wi.Groups.Translate(typeof(NTAccount));
var roles = groups.Select(x => new Claim(JwtClaimTypes.Role, x.Value));
id.AddClaims(roles);
await HttpContext.SignInAsync(
IdentityServerConstants.ExternalCookieAuthenticationScheme,
new ClaimsPrincipal(id),
props);
return Redirect(props.RedirectUri);
}
else
{
// trigger windows auth
// since windows auth don't support the redirect uri,
// this URL is re-triggered when we call challenge
return Challenge("Windows");
}
}

How to send token from ASP.net Web API to react js?

I want to implement JWT Authentication in react js using web api.
I had created the JWT Authentication in web api.
It worked totally fine on Postman as I tested it.
When I am using it with react js the API is being hitted.
Now the problem is how do I send the token to react js and how do I fetch the token in react js
This is my Login Controller in web api
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Security.Claims;
using System.Web;
using System.Web.Http;
using System.Web.Http.Cors;
using WEBAPI_JWT_Authentication.Models;
namespace WEBAPI_JWT_Authentication.Controllers
{
[EnableCors(origins: "*", headers: "*", methods: "*")]
public class LoginController : ApiController
{
[HttpPost]
public IHttpActionResult Authenticate([FromBody] LoginRequest login)
{
var loginResponse = new LoginResponse { };
LoginRequest loginrequest = new LoginRequest { };
loginrequest.Username = login.Username.ToLower();
loginrequest.Password = login.Password;
IHttpActionResult response;
HttpResponseMessage responseMsg = new HttpResponseMessage();
bool isUsernamePasswordValid = false;
if(login != null)
isUsernamePasswordValid=loginrequest.Password=="test" ? true:false;
// if credentials are valid
if (isUsernamePasswordValid)
{
string token = createToken(loginrequest.Username);
var responseJSON = token;
//return the token
return Ok(responseJSON);
}
else
{
// if credentials are not valid send unauthorized status code in response
loginResponse.responseMsg.StatusCode = HttpStatusCode.Unauthorized;
response = ResponseMessage(loginResponse.responseMsg);
return response;
}
}
private string createToken(string username)
{
//Set issued at date
DateTime issuedAt = DateTime.UtcNow;
//set the time when it expires
DateTime expires = DateTime.UtcNow.AddDays(7);
//http://stackoverflow.com/questions/18223868/how-to-encrypt-jwt-security-token
var tokenHandler = new JwtSecurityTokenHandler();
//create a identity and add claims to the user which we want to log in
ClaimsIdentity claimsIdentity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, username)
});
const string sec = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1";
var now = DateTime.UtcNow;
var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));
var signingCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(securityKey,Microsoft.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256Signature);
//create the jwt
var token =
(JwtSecurityToken)
tokenHandler.CreateJwtSecurityToken(issuer:"http://localhost:50191",audience:"http://localhost:50191",
subject: claimsIdentity, notBefore: issuedAt, expires: expires, signingCredentials: signingCredentials);
var tokenString = tokenHandler.WriteToken(token);
return tokenString;
}
}
}
This is where I am fetching the token in react js
function login(username, password) {
return fetch(`${API_URL}/Login`, {username, passowrd})
.then(response => {
debugger;
if (!response.ok) {
return response;
}
return response.json();
})
.then(user => {
debugger;
// login successful if there's a jwt token in the response
if (user && user.token) {
// store user details and jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem('user', JSON.stringify(user));
}
return user;
});
}
Rather than this data if anyone knows how to send token to react js and how to fetch that token in react js, please do tell.
The way I do is to create a Response class
public class Response
{
public string Status { get; set; }
public string Message { get; set; }
public object Token { get; set; }
}
Depending on your need what you want to define in this class, for my cases, Status and Message is used to update progress status to front end.
You store your tokendictionary in class Response, and return it to the function. Lets say:
[HttpPost]
public IHttpActionResult Authenticate([FromBody] LoginRequest login)
{
var loginResponse = new LoginResponse { };
LoginRequest loginrequest = new LoginRequest { };
loginrequest.Username = login.Username.ToLower();
loginrequest.Password = login.Password;
IHttpActionResult response;
HttpResponseMessage responseMsg = new HttpResponseMessage();
bool isUsernamePasswordValid = false;
if(login != null)
isUsernamePasswordValid=loginrequest.Password=="test" ? true:false;
// if credentials are valid
if (isUsernamePasswordValid)
{
string token = createToken(loginrequest.Username);
Response Resp = new Response
{
Status = "Success",
Message = "User Login Successfully Change the Status Message here",
Token = tokenDictonary, //where you return token
};
return
}
else
{
// if credentials are not valid send unauthorized status code in response
loginResponse.responseMsg.StatusCode = HttpStatusCode.Unauthorized;
response = ResponseMessage(loginResponse.responseMsg);
return response;
}
}
and in your front end, you fetch your api.
I show you axios example
axios.post('http://yoururl', {
Token: this.state.Token,
})
.then(result => {
if (result.data.Status === 'Success') {
localStorage.setItem('Nameyourvariablehere', result.data.Token.tokentype);
// generate more if your token has more field
and then you are able to check your localStorage via getItem and setItem, I believe you know what to do for the following steps
Actually the way you create token is different from mine, I kind of follow this example.

spring boot oauth & facebook reference

I'm starting to play around with OAUTH2 in spring boot. I'm currently trying to edit the simple example on this website: https://spring.io/guides/tutorials/spring-boot-oauth2/
But I would like to display more of the person who logs in like email, age and stuff that can be found here:
https://developers.facebook.com/docs/facebook-login/permissions/
Could you guys give me any advice to get any of the information that's described in the url above?
This part is used as login:
<div class="container" ng-show="!home.authenticated">
With Facebook: click here
</div>
Then there is this part in the angular code:
<script type="text/javascript">
angular
.module("app", [])
.config(
function($httpProvider) {
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
}).controller("home", function($http, $location) {
var self = this;
$http.get("/user").success(function(data) {
self.user = data.userAuthentication.details.name;
// i added the following line self.details =
self.details = data.userAuthentication.details
self.authenticated = true;
$http.get("")
}).error(function() {
self.user = "N/A";
self.authenticated = false;
});
self.logout = function() {
$http.post('logout', {}).success(function() {
self.authenticated = false;
$location.path("/");
}).error(function(data) {
console.log("Logout failed")
self.authenticated = false;
});
};
});
</script>
Then I put home.details on the screen and it shows this:{"name":"firstname lastname","id":"some-numbers"}
I think /user refers to a method in the socialapplication.java. So I'm wondering where do I edit what information get's retrieved from facebook.
#SpringBootApplication
#RestController
#EnableOAuth2Client
public class SocialApplication extends WebSecurityConfigurerAdapter {
#Autowired
OAuth2ClientContext oauth2ClientContext;
#RequestMapping("/user")
public Principal user(Principal principal) {
return principal;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.antMatcher("/**").authorizeRequests().antMatchers("/", "/login**", "/webjars/**").permitAll().anyRequest()
.authenticated().and().exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/")).and().logout()
.logoutSuccessUrl("/").permitAll().and().csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and()
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
// #formatter:on
}
public static void main(String[] args) {
SpringApplication.run(SocialApplication.class, args);
}
#Bean
public FilterRegistrationBean oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(filter);
registration.setOrder(-100);
return registration;
}
private Filter ssoFilter() {
OAuth2ClientAuthenticationProcessingFilter facebookFilter = new OAuth2ClientAuthenticationProcessingFilter(
"/login/facebook");
OAuth2RestTemplate facebookTemplate = new OAuth2RestTemplate(facebook(), oauth2ClientContext);
facebookFilter.setRestTemplate(facebookTemplate);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(facebookResource().getUserInfoUri(),
facebook().getClientId());
tokenServices.setRestTemplate(facebookTemplate);
facebookFilter.setTokenServices(
new UserInfoTokenServices(facebookResource().getUserInfoUri(), facebook().getClientId()));
return facebookFilter;
}
#Bean
#ConfigurationProperties("facebook.client")
public AuthorizationCodeResourceDetails facebook() {
return new AuthorizationCodeResourceDetails();
}
#Bean
#ConfigurationProperties("facebook.resource")
public ResourceServerProperties facebookResource() {
return new ResourceServerProperties();
}
Here is the YAML file.
facebook:
client:
clientId: xxxxxxxxxx
clientSecret: xxxxxxxxxxxxxxxxxxxxxx
accessTokenUri: https://graph.facebook.com/oauth/access_token
userAuthorizationUri: https://www.facebook.com/dialog/oauth
tokenName: oauth_token
authenticationScheme: query
clientAuthenticationScheme: form
resource:
userInfoUri: https://graph.facebook.com/me
logging:
level:
org.springframework.security: DEBUG
I'm really at a loss here and hope someone can help me to retrieve more information from facebook.
Thanks for any kind of help!

Getting status 500 when using angularjs $http to get data from server

I am working on an asp.net mvc application and I am using Entity Framework and AngularJS in it. I am using AngularJS's $http service to call an action method and retrieve data from the server. The correct data is retrieved from the server (I confirmed this by debugging), but somehow an error occurs after the action method returns the retrieved data and the error callback function is fired instead of the success callback function. And then I get a status 500 in the browser's console.
Here are the involved blocks of codes:
(From angularjs controller)
$http({
url: rootUrl + "User/GetUser",//'#Url.Action("GetUser","User")',
method: 'POST',
params: {
uname: $scope.username,
pword: $scope.pass
}
}).then(function (response) {
alert('success!');
$scope.user = response.data;
if ($scope.user.Fullname != undefined) {
$http({
url: rootUrl + "Session/Set",
method: "POST",
data: {
"key": "curr_user",
"value": JSON.stringify($scope.user)
}
});
window.location.href = rootUrl + 'Product/List/';
} else {
//invalid login
$("input[name='password']").select();
$("#validation-summary").html("Wrong email or password.");
$scope.invalidlogin = true;
$(btnLogin).removeClass('disabled');
$(btnLogin).text("Submit");
}
(From mvc controller)
[HttpPost]
public JsonResult GetUser(string uname, string pword)
{
JBManager manager = null;
using (SE_Context db = new SE_Context())
{
try
{
manager = db.Managers
.Include("Transactions.Items")
.Where(m => m.Username == uname && m.Password == pword)
.FirstOrDefault();
//At this point, manager has the desired data
return Json(manager, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
return null;
}
}
}
And here's a screenshot of the error in the browser:
Would really appreciate any help. Thanks!
UPDATE:
Everything was working fine before I used Entity Framework. (Just in case it has something to do with the issue)
I think your issue is nested objects.You can flatten object graphs that contain nested objects using DTOs (Data Transfer Objects).
You can just try simple example as like below.If it'll work then you need to extend it to work with your EF query.
public class MyDto
{
public string Name { get; set; }
}
[HttpPost]
public JsonResult GetUser(string uname, string pword)
{
JBManager manager = null;
using (SE_Context db = new SE_Context())
{
try
{
//construct the DTO here
manager = db.Managers.Select(a=> new MyDto(
{
Name = a.Name
})).FirstOrDefault(m => m.Username == uname && m.Password == pword);
return Json(manager, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
return null;
}
}
}
You can read more about DTOs here : Create Data Transfer Objects (DTOs)

Resources