Cortana - OAuth 2 connected service - authentication

We have created an OAuth2 provider as an app service as given in below example.
https://learn.microsoft.com/en-us/aspnet/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server
Step 2:
We need to configure this endpoints to Cortana connected service OAuth2 end points to get the access token from Cortana app
Cortana Auth Config
Sample Code in the Auth provider authorize function is below:
public ActionResult Login()
{
var authentication = HttpContext.GetOwinContext().Authentication;
if (Request.HttpMethod == "POST")
{
var isPersistent = !string.IsNullOrEmpty(Request.Form.Get("isPersistent"));
if (!string.IsNullOrEmpty(Request.Form.Get("submit.Signin")))
{
authentication.SignIn(
new AuthenticationProperties { IsPersistent = isPersistent },
new ClaimsIdentity(new[] { new Claim(ClaimsIdentity.DefaultNameClaimType, Request.Form["username"]) }, "Application"));
}
}
return View();
// Response.End();
}
Authorize
public ActionResult Authorize()
{
if (Response.StatusCode != 200)
{
return View("AuthorizeError");
}
var authentication = HttpContext.GetOwinContext().Authentication;
var ticket = authentication.AuthenticateAsync("Application").Result;
var identity = ticket != null ? ticket.Identity : null;
if (identity == null)
{
authentication.Challenge("Application");
return new HttpUnauthorizedResult();
}
var scopes = (Request.QueryString.Get("scope") ?? "").Split(' ');
if (Request.HttpMethod == "POST")
{
if (!string.IsNullOrEmpty(Request.Form.Get("submit.Grant")))
{
identity = new ClaimsIdentity(identity.Claims, "Bearer", identity.NameClaimType, identity.RoleClaimType);
foreach (var scope in scopes)
{
identity.AddClaim(new Claim("urn:oauth:scope", scope));
}
authentication.SignIn(identity);
}
//if (!string.IsNullOrEmpty(Request.Form.Get("submit.Login")))
//{
// authentication.SignOut("Application");
// authentication.Challenge("Application");
// return new HttpUnauthorizedResult();
//}
}
return View();
}
I want to change this code in a way that it could be able to send back data to Cortana channel. Please let know your suggestions.
Thanks

Related

How to make Identity Server return both access and identity tokens?

I need both because I have lots of claims, I want to store tokens in client-side database, use only token without claims (identity one, I guess) and on request I will replace the small one (which was sent, it has no claims) with big one, this way requests are not that big and I still can use claims for API access. Currently only access token is returned, identity token is null. Here's identity config:
public IdentityConfig(IConfiguration config)
{
ApiScopes = new List<ApiScope>();
Clients = new List<Client>();
foreach (var apiScope in config.GetSection("IdentityConfig").GetSection("ApiScopes").GetChildren())
{
ApiScopes.Add(new ApiScope(apiScope.Value));
}
foreach (var client in config.GetSection("IdentityConfig").GetSection("Clients").GetChildren())
{
Clients.Add(new Client
{
ClientId = client["Id"],
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,//can be moved to config
ClientSecrets = { new Secret(client["Secret"].Sha256()) },//can be multiple
AllowedScopes = new List<string> { IdentityServerConstants.StandardScopes.OpenId, "apiScope1", IdentityServerConstants.StandardScopes.Profile }//client.GetSection("AllowedScopes").GetChildren().Select(a => a.Value).ToList()
});
}
Resources = new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email()
};
}
I use token endpoint like this:
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("https://localhost:5001");
var tokenClient = new TokenClient(client, new TokenClientOptions
{
ClientId = "id1",
ClientSecret = "secret1",
Address = disco.TokenEndpoint,
});
var tokenResponse = await tokenClient.RequestPasswordTokenAsync("ssAdmin", "123abc");
I wrote custom password validator because I had legacy system which had it's own implementation of auth, not sure if it's needed but just in case:
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
{
try
{
var user = await _userRepository.FindByNameAsync(context.UserName);
if (user != null)
{
if (!string.IsNullOrWhiteSpace(user.LegacyPasswordHash))
{
var hashedPassword = GetLegacyHash(context.Password);
if (hashedPassword == user.LegacyPasswordHash)
{
context.Result = new GrantValidationResult(
subject: user.Id,
authenticationMethod: "custom",
claims: GetUserClaims(user));
return;
}
}
if (await _userRepository.CheckPasswordAsync(user, context.Password))
{
context.Result = new GrantValidationResult(
subject: user.Id,
authenticationMethod: "custom",
claims: GetUserClaims(user));
return;
}
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Incorrect password");
return;
}
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "User does not exist.");
return;
}
catch (Exception ex)
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Invalid username or password");
}
}

How to redirect already authorized user back to the Previous page in identity server 4 .NET CORE?

Below is the code added in Auth Server and Client machine
If any more info needed on this please comment.
StartUp.cs -- Auth Server
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
var builder = services.AddIdentityServer(options =>
{
options.EmitStaticAudienceClaim = true;
})
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients)
.AddResourceOwnerValidator<ResourceOwnerPasswordValidator>()
/*.AddProfileService<ProfileService>()*/;
builder.AddDeveloperSigningCredential();
services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();
//services.AddTransient<IProfileService, ProfileService>();
}
StartUp.cs -- Client App
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = "https://localhost:44356/";
options.ClientId = "TestIdpApp";
options.ResponseType = "code";
//options.UsePkce = false;
//options.CallbackPath = new PathString("...")
options.Scope.Add("openid");
options.Scope.Add("profile");
options.SaveTokens = true;
options.ClientSecret = "secret";
});
}
ResourceOwnerPasswordValidator.cs -- Auth Server
public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
{
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
{
try
{
UserInfo user = await GetUserDetails(context.UserName, context.Password).ConfigureAwait(false);
if (user != null && string.IsNullOrEmpty(user.ErrorMessage))
{
//set the result
context.Result = new GrantValidationResult(
subject: user.UserId.ToString(),
authenticationMethod: "custom",
claims: GetUserClaims(user));
return;
}
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, user.ErrorMessage);
return;
}
catch (Exception ex)
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Invalid username or password");
}
}
//build claims array from user data
public static Claim[] GetUserClaims(UserInfo user)
{
return new Claim[]
{
new Claim("user_id", user.UserId.ToString() ?? ""),
new Claim(JwtClaimTypes.Name, (!string.IsNullOrEmpty(user.FirstName) && !string.IsNullOrEmpty(user.Surname)) ? (user.FirstName + " " + user.Surname) : ""),
new Claim(JwtClaimTypes.GivenName, user.FirstName ?? ""),
new Claim(JwtClaimTypes.FamilyName, user.Surname ?? ""),
new Claim(JwtClaimTypes.Email, user.Email ?? "")
};
}
}
Login Logic ---
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model, string button)
{
// check if we are in the context of an authorization request
var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
if (button == "reset")
{
return Redirect("ResetPasswordWithoutCode");
}
else
{
// the user clicked the "cancel" button
if (button == "cancel")
{
if (context != null)
{
// if the user cancels, send a result back into IdentityServer as if they
// denied the consent (even if this client does not require consent).
// this will send back an access denied OIDC error response to the client.
await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied);
// we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
if (context.IsNativeClient())
{
// The client is native, so this change in how to
// return the response is for better UX for the end user.
return this.LoadingPage("Redirect", model.ReturnUrl);
}
return Redirect(model.ReturnUrl);
}
else
{
// since we don't have a valid context, then we just go back to the home page
return Redirect("~/");
}
}
if (ModelState.IsValid)
{
ResourceOwnerPasswordValidationContext context1 = new ResourceOwnerPasswordValidationContext();
context1.UserName = model.Username;
context1.Password = model.Password;
// validate username/password against in-memory store
await _resourceOwner.ValidateAsync(context1);
if (context1.Result.Subject!=null && context1.Result.Subject.Identity.IsAuthenticated)
{
var user = await ResourceOwnerPasswordValidator.GetUserDetails(model.Username,model.Password).ConfigureAwait(false);
await _events.RaiseAsync(new UserLoginSuccessEvent(user.Username, user.UserId, user.Username, clientId: context?.Client.ClientId));
// only set explicit expiration here if user chooses "remember me".
// otherwise we rely upon expiration configured in cookie middleware.
AuthenticationProperties props = null;
if (AccountOptions.AllowRememberLogin && model.RememberLogin)
{
props = new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration)
};
};
// issue authentication cookie with subject ID and username
var isuser = new IdentityServerUser(user.UserId)
{
DisplayName = user.Username
};
await HttpContext.SignInAsync(isuser, props);
if (context != null)
{
if (context.IsNativeClient())
{
// The client is native, so this change in how to
// return the response is for better UX for the end user.
return this.LoadingPage("Redirect", model.ReturnUrl);
}
// we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
return Redirect(model.ReturnUrl);
}
// request for a local page
if (Url.IsLocalUrl(model.ReturnUrl))
{
return Redirect(model.ReturnUrl);
}
else if (string.IsNullOrEmpty(model.ReturnUrl))
{
return Redirect("~/");
}
else
{
// user might have clicked on a malicious link - should be logged
throw new Exception("invalid return URL");
}
}
await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId: context?.Client.ClientId));
ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage);
}
// something went wrong, show form with error
var vm = await BuildLoginViewModelAsync(model);
return View(vm);
}
}
I'm calling a webapi to check the credentials and return info about user like First name last name.
RedirectUri - https://localhost:44356/Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3DTestIdpApp%26redirect_uri%3Dhttps%253A%252F%252Flocalhost%253A44335%252Fsignin-oidc%26response_type%3Dcode%26scope%3Dopenid%2520profile%26code_challenge%3DLEZaLWC8ShzJz6LGUsdeUPr974clsUSYVPXWDNmbwOE%26code_challenge_method%3DS256%26response_mode%3Dform_post%26nonce%3D637360236231259886.MmIxNjlhODMtZTJhYy00YzUzLTliYjMtZWJmNzM3ZjRiM2VlZmYwYzI2MDAtNWRjYS00NThlLWI4MjAtY2ViYjgxY2RlYmZi%26state%3DCfDJ8LFJDL-5o71Ao2KksnBDgPVrH1DIIiM9LZSGUG43HRwLS6OjGUiGPwZ_xxT1RVryTZh7z3zwezVbdiy1L94mFlWausuYQrDNTWtzxrpTf2CrKjHRjcUIyNt5tX_g-yZYkWvxzCiyrpxnp7cctbNGoCmj_kqidhxCWsZee_26c3eVqfJfH7XEDfKUMj2BHeKQe_Ar9f2SkZJ0SBuy6MBe6zpU7-DDOYotDn-oO5zrtaHL8GCZfSqckqalL5yaGeolZ1ZDcubY01InyrBh1NwlVQRdGZRRWIZ-WnqqFKrTboQyw4rQswR-7BaLTtL8QitRkUmwS17LBLUvXKRBs8C0NUsX9HyREnmCVG2qW6s2AVpnE4iSt4XVSRcY-crXml2FjA%26x-client-SKU%3DID_NETSTANDARD2_0%26x-client-ver%3D5.5.0.0
You could have something like this in your GET Login action:
if (this.User.Identity.IsAuthenticated)
{
return Redirect(returnUrl);
}

HttpContext.SignInAsync not starting a new session for database users in identity server 4

I want to validate and use my database users in identity server 4.Here is my customized login code in Account Controller.
if (ModelState.IsValid)
{
// validate username/password against my user repository class, and get the user's info
var user = await _users.FindAsync(model.Username, model.Password);
if(user != null)
{
await _events.RaiseAsync(new UserLoginSuccessEvent(user.LoginId, user.SubjectId, user.FullName, clientId: context?.Client.ClientId));
AuthenticationProperties props = null;
if (AccountOptions.AllowRememberLogin && model.RememberLogin)
{
props = new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration)
};
};
var isuser = new IdentityServerUser(user.SubjectId)
{
DisplayName = user.FullName,
AdditionalClaims=user.Claims.ToList(),
AuthenticationTime=DateTime.UtcNow
};
await HttpContext.SignInAsync(isuser, props);
if (context != null)
{
if (context.IsNativeClient())
{
return this.LoadingPage("Redirect", model.ReturnUrl);
}
return Redirect(model.ReturnUrl);
}
// request for a local page
if (Url.IsLocalUrl(model.ReturnUrl))
{
return Redirect(model.ReturnUrl);
}
else if (string.IsNullOrEmpty(model.ReturnUrl))
{
return Redirect("~/");
}
else
{
// user might have clicked on a malicious link - should be logged
throw new Exception("invalid return URL");
}
}
await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId:context?.Client.ClientId));
ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage);
}
I have created the Profile service class and configured the startup class like this
services.AddIdentityServer()
.AddInMemoryClients(InMemoryConfig.GetClients())
.AddProfileService<ProfileService>()
.AddInMemoryApiScopes(InMemoryConfig.GetApiScopes())
.AddInMemoryIdentityResources(InMemoryConfig.GetIdentityResources())
.AddDeveloperSigningCredential();
services.AddTransient<IProfileService, ProfileService>();
services.AddTransient<IUserRepository, UserRepository>();
But the user session never starts, takes me back to the login page and Profile service never gets called.
Where am I going wrong?
Sorry, guys my bad. This problem arises when SSL is disabled for my identity server 4 application. I enabled SSL for my application and everything started working fine.

Prevent users to have multiple sessions with JWT Tokens

I am building an application which uses JWT bearer authentication in ASP.NET Core. I need to prevent users to have multiple sessions open at the same time. I am wondering if there is way using Microsoft.AspNetCore.Authentication.JwtBearer middleware to list out all the tokens of an user and then verify if there are other tokens issued for that user in order to invalidate the incoming authentication request.
If the claims are able to be validated on the server, I guess that in order to do that, the server has a record of those claims and the user who owns them. Right?
Any ideas How Can I achieve this?
I have achieved my goal, saving in the db the timestamp when the user login, adding that timestamp in the payload of the token and then adding an additional layer of security to validate the JWT against the db, returning 401 if the timestamp doesn't match. This is the code implemented with .net Core 2.0 if someone need it.
Controller:
[HttpPost]
[Route("authenticate")]
public async Task<IActionResult> AuthenticateAsync([FromBody] UserModel user)
{
try
{
.......
if (userSecurityKey != null)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
// This claim allows us to store information and use it without accessing the db
new Claim("userSecurityKey", userDeserialized.SecurityKey.ToString()),
new Claim("timeStamp",timeStamp),
new Claim("verificationKey",userDeserialized.VerificationKey.ToString()),
new Claim("userName",userDeserialized.UserName)
}),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key),
SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
// Updates timestamp for the user if there is one
VerificationPortalTimeStamps userTimeStamp = await _context.VerificationPortalTimeStamps.AsNoTracking().FirstOrDefaultAsync(e => e.UserName == userDeserialized.UserName);
if (userTimeStamp != null)
{
userTimeStamp.TimeStamp = timeStamp;
_context.Entry(userTimeStamp).State = EntityState.Modified;
await _context.SaveChangesAsync();
}
else
{
_context.VerificationPortalTimeStamps.Add(new VerificationPortalTimeStamps { TimeStamp = timeStamp, UserName = userDeserialized.UserName });
await _context.SaveChangesAsync();
}
// return basic user info (without password) and token to store client side
return Json(new
{
userName = userDeserialized.UserName,
userSecurityKey = userDeserialized.SecurityKey,
token = tokenString
});
}
return Unauthorized();
}
catch (Exception)
{
return Unauthorized();
}
}
Then, to configure the JWT Bearer Authentication with .Net Core 2.0
Startup.cs:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider provider)
{
.................
app.UseAuthentication();
app.UseMvc();
...........
}
To configure the JWT Bearer authentication:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
............
var key = Configuration["AppSettings:Secret"];
byte[] keyAsBytes = Encoding.ASCII.GetBytes(key);
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(o =>
{
o.RequireHttpsMetadata = false;
o.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(keyAsBytes),
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true
};
o.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
if (Configuration["AppSettings:IsGodMode"] != "true")
context.Response.StatusCode = 401;
return Task.FromResult<object>(0);
}
};
o.SecurityTokenValidators.Clear();
o.SecurityTokenValidators.Add(new MyTokenHandler());
});
services.AddMvc()
.AddJsonOptions(opt =>
{
opt.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
});
var provider = services.BuildServiceProvider();
return provider;
}
Then, we implement the custom validation in the controller as follows:
[Authorize]
[HttpGet]
[Route("getcandidate")]
public async Task<IActionResult> GetCandidateAsync()
{
try
{
.....
//Get user from the claim
string userName = User.FindFirst("UserName").Value;
//Get timestamp from the db for the user
var currentUserTimeStamp = _context.VerificationPortalTimeStamps.AsNoTracking().FirstOrDefault(e => e.UserName == userName).TimeStamp;
// Compare timestamp from the claim against timestamp from the db
if (User.FindFirst("timeStamp").Value != currentUserTimeStamp)
{
return NotFound();
}
...........
}
catch (Exception)
{
return NotFound();
}
}

KnockoutJS + WebAPI 2 Token Authentication - maintain login state until token expires

I'm fairly new to token based authentication and I have a problem of how to maintain login state after I login.
I want to create a SPA website for which I am using Knockoutjs for my front end and SammyJS for routing and changing the views.
After I login in and get the token I store it in localStorage and set the username into an observable which I am displaying.
My problem is that after I close the tab or browser and I go back to the site, the token is in the localStorage but I can't see the user logged in.
I want to maintain the login state until the token expires. My question is what should I do with the token from the localStorage when I enter the site in order to maintain the login state of that user?
Do I need to make something in the startup class or to check if that user exists in the DB?
Thanks in advance!
Here is my code:
StartupAuth.cs
[assembly: OwinStartup(typeof(EventHub.PL.WebUI.Startup))] namespace EventHub.PL.WebUI {
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get;private set; }
public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
public const string TokenEndpointPath = "/api/token";
public static string PublicClientId { get; private set; }
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString(TokenEndpointPath),
Provider = new ApplicationOAuthProvider(PublicClientId),
//AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
//app.UseOAuthBearerTokens( OAuthOptions );
app.UseOAuthAuthorizationServer(OAuthOptions);
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
}
}
AccountController.cs
[HttpPost]
[AllowAnonymous]
[Route("Login")]
public async Task<IHttpActionResult> Login(LoginUser model)
{
var request = HttpContext.Current.Request;
var tokenServiceUrl = request.Url.GetLeftPart(UriPartial.Authority) + request.ApplicationPath + "/api/Token";
using (var client = new HttpClient())
{
var requestParams = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username", model.Email),
new KeyValuePair<string, string>("password", model.Password)
};
var requestParamsFormUrlEncoded = new FormUrlEncodedContent(requestParams);
var tokenServiceResponse = await client.PostAsync(tokenServiceUrl, requestParamsFormUrlEncoded);
var responseString = await tokenServiceResponse.Content.ReadAsStringAsync();
var json = JsonConvert.DeserializeObject<TokenResponse>(responseString);
var responseCode = tokenServiceResponse.StatusCode;
if (responseCode == HttpStatusCode.OK)
{
RegisterUser user = userRepository.GetNameById(json.Id);
var data = new
{
status = "success",
json.access_token,
user.Lastname
};
return Json(data);
}
return Json(new { status = "failed" });
}
}
here is the KO part:
var LoginApp = function () {
var instance = this;
instance.mainViewModel = new MainViewModel();
instance.loginViewModel = new LoginViewModel();
instance.loginRepository = new LoginRepository();
instance.loginViewModel.signIn = function() {
$('.loader-header').show();
var postData = {
email: instance.loginViewModel.email(),
password: instance.loginViewModel.password
}
instance.loginRepository.SignIn(SignInSuccess, postData);
};
instance.SignInSuccess = function(response) {
if (response.status === 'success') {
instance.mainViewModel.username(response.Lastname);
instance.mainViewModel.isVisible(true);
var userData = {
token: response.access_token,
username: response.Lastname
};
localStorage.setItem('AuthorizationData', JSON.stringify(userData));
$('.loader-header').hide();
dialog.close();
} else {
$('.loader-header').hide();
}
};
instance.init = function () {
ko.applyBindings(instance.loginViewModel, document.getElementById("signin-form"));
ko.applyBindings(instance.mainViewModel, document.getElementById("main-wrapper"));
}
instance.init();
}
$(document).ready(function () {
var loginApp = LoginApp();
});
UPDATE
here is my routing also
var appRoot = root;
(function ($) {
var app = $.sammy('#page', function () {
this.get('#/home', function (context) {
document.title = 'Home - ' + title;
var url = getUrlFromHash(context.path);
loadView(url, new MainViewModel(), MainApp);
//context.load(url).swap();
});
this.get('#/about', function (context) {
var url = getUrlFromHash(context.path);
loadView(url, new AboutViewModel(), AboutApp);
});
this.get('#/manage', function (context) {
var url = getUrlFromHash(context.path);
loadView(url, new AboutViewModel(), AboutApp);
});
});
$(function () {
app.run('#/home');
});
})(jQuery);
function loadView(url, viewModel, callback) {
$.get(url, function (response) {
var $container = $('#page');
//var $view = $('#page').html(response);
$container.html(response);
callback();
});
}
function getUrlFromHash(hash) {
var url = hash.replace('#/', '');
if (url === appRoot)
url = 'home';
return url;
}
Right now all you're doing is storing the user's credentials in localStorage but not using them to perform authorization. One alternative is to use the Sammy.OAuth2 plugin (which you can find it here).
You can define a route to make the authentication like:
app.post("#/oauth/login", function(context) {
this.load('http://yourwebsite/login',
{
cache: false,
type: 'post',
data: {
email: $("input[name=email]").val(),
password: $("input[name=password]").val()
}
})
.then(function(content) {
if(content != false){
if(app.getAccessToken() == null){
app.setAccessToken(token());
}
}else{
app.trigger("oauth.denied");
return false;
}
});
});
In 'protected' routes you can check if the user is already logged in like this:
app.get("#/profile", function(context) {
if(app.getAccessToken() != null)
context.render('view/profile.template');
else
this.requireOAuth();
});
This examples will have to be modified to populate the token according to your scenario. Here's a complete tutorial on Sammy.Oath2.