I am trying to create a demo project which uses .Net ASP.Net WebAPI and KnockoutJs as the front end. I have created the controller methods that listen for the /token post, and validates a user, and returns a token. This is done from an Ajax Post from the Knockout View Model.
This code works. However, when I get 200 back (Success) from the webApi, I then redirect to a controller method, decorated with a [Authorize]. And that's where I hit a 401 - not authorised.
Login()
{
var data = {
username : this.login.emailAddress(),
password : this.login.password(),
RememberMe: this.login.rememberMe(),
grant_type: "password"
}
return $.ajax({
type: "POST",
data: data,
dataType: "json",
url: "/token",
contentType: "application/json"
}).done((reply) => {
window.location.href = "/Home/AnotherThing";
});
}
I think the issue is - I get a response back from my /token (login) call, but do nothing with it. I'm not sure what to do with the token. I stupidly thought that OAuth would somehow put the token into my headers, and they would be there magically. I was wrong.
So, I've been looking for an example, and then best I can find is Here
But this means I am going to have a LOT of repeated code, on each view model
Extract:
function ViewModel() {
var self = this;
var tokenKey = 'accessToken';
var RefTokenKey = 'refreshToken';
self.result = ko.observable();
self.user = ko.observable();
self.token = ko.observable();
self.refreshToken = ko.observable();
function showError(jqXHR) {
self.result(jqXHR.status + ': ' + jqXHR.statusText);
}
self.callApi = function () {
self.result('');
var token = sessionStorage.getItem(tokenKey);
var headers = {};
if (token) {
headers.Authorization = 'Bearer ' + token;
}
$.ajax({
type: 'GET',
url: '/api/values',
headers: headers
}).done(function (data) {
self.result(data);
}).fail(showError);
}
self.callToken = function () {
self.result('');
var loginData = {
grant_type: 'password',
username: self.loginEmail(),
password: self.loginPassword()
};
$.ajax({
type: 'POST',
url: '/Token',
data: loginData
}).done(function (data) {
self.user(data.userName);
// Cache the access token in session storage.
sessionStorage.setItem(tokenKey, data.access_token);
var tkn = sessionStorage.getItem(tokenKey);
$("#tknKey").val(tkn);
}).fail(showError);
}
}
var app = new ViewModel();
ko.applyBindings(app);
This seems to be part of what I am missing:
sessionStorage.setItem(tokenKey, data.access_token);
var tkn = sessionStorage.getItem(tokenKey);
$("#tknKey").val(tkn);
Would I need every view model to have the code that then goes to the sessionStorage, and get the token?
So, this:
var token = sessionStorage.getItem(tokenKey);
var headers = {};
if (token) {
headers.Authorization = 'Bearer ' + token;
}
$.ajax({
type: 'GET',
url: '/api/values',
headers: headers
}).done(function (data) {
self.result(data);
}).fail(showError);
}
It seems like a lot of code.. Is this the right way to go?
Ok, so what you could do is attach the bearer token to each of your HTTP requests. I assume you're using jQuery there? If that's the case you could leverage the beforeSend config param.
Extract a reusable method such as this:
function onBeforeSend(xhr, settings) {
var token = sessionStorage.getItem(tokenKey);
if (token) {
xhr.setRequestHeader('Authorization', 'Bearer ' + token );
}
}
And then simply attach that method to each of your $.ajax calls that require the token, like this:
$.ajax({
type: 'GET',
url: '/api/values',
headers: headers,
beforeSend: onBeforeSend
}).done(function (data) {
self.result(data);
}).fail(showError);
The onBeforeSend function obviously needs to be accessible by your ajax call (I'm not a knockout guy so I don't know if it has any constructs such as services, but if not, you could namespace it for example to avoid making it a global function, but your code organization is up to you).
This way you'll only have to add the beforeSend: onBeforeSend bit to each request that requires auth and it will avoid unnecessary code duplication.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With