I am implementing authentication where I added a token in response from server side.
I am trying to read this header value returned from server in angularjs however I don't see this header value present. Here is my javascript console.
EDIT:
return base.SendAsync(request, cancellationToken).ContinueWith(task =>
{
var response = task.Result;
if (response.RequestMessage.Headers.Contains(TOKEN_NAME))
{
string token = response.RequestMessage.Headers.GetValues(TOKEN_NAME).FirstOrDefault();
response.Headers.Add("Access-Control-Expose-Headers", TOKEN_NAME);
response.Headers.Add(TOKEN_NAME, token);
}
return response;
});
Is the access/authenticate endpoint returning the token as data in the success method or is the token being set in the server side code?
-Update-
If you set the token in HttpContext.Current.Response.AppendHeader('X-Token', "<some token value">); You should be able to grab it in your $http promise
$http.get("<api endpoint>").then(function(data){
$log.log("data.config", data.config);
$log.log("data.headers()", data.headers());
$log.log("X-Token Header", data.headers()['x-token']);
});
data.config is the headers sent to the server such as the accept and any authorization headers.
data.headers() is the function that returns all headers that were set server side.
response.Headers.Add("Access-Control-Expose-Headers", TOKEN_NAME); this line will ensure this header is available to the server
So if I understand correctly your passing x-token in the header of the api request and the Delegating Handler is looking for TOKEN_NAME and then resetting it and then your trying to access it in the promise of $http. I just put together a test for this case and im getting back x-token;
-Sample angular app
(function () {
var app = angular.module('app', []);
app.config(function ($httpProvider) {
$httpProvider.defaults.headers.common["x-token"] = "";
});
app.controller('home', function ($http, $log) {
$http.get('/api/car/get')
.then(function (response) {
$log.log("Response headers",response.headers());
$log.log(response.headers()["x-token"]);
});
});
})();
-Console window
-CustomDelegatingHandler i dont use the variable token because I dont have a token endpoint to get one. Instead I just passed back a random GUID.
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
return await base.SendAsync(request, cancellationToken).ContinueWith(task =>
{
var response = task.Result;
//Are you sure your passing this check by setting the x-token common header in your angular $http requests?
if (response.RequestMessage.Headers.Contains(TOKEN_NAME))
{
string token = response.RequestMessage.Headers.GetValues(TOKEN_NAME).FirstOrDefault();
response.Headers.Add("Access-Control-Expose-Headers", TOKEN_NAME);
response.Headers.Add(TOKEN_NAME, Guid.NewGuid().ToString());
}
return response;
}, cancellationToken);
}
Related
I have an interceptor:
axios.interceptors.response.use(doSomething, error => handleError(error));
I can access all the information about the request in err.config for "normal" errors
const handleError = err => {
if(err.config) {
// use values from err.config
}
}
However, Cancel error doesn't contain it. It only contains message.
Is it possible to access request parameters in the interceptor when the request has been cancelled?
If you need something in particular when handling the cancel event - you need to provide that information at the place where you cancel the request:
const token = CancelToken.source()
const request = axios.get('/url', { cancelToken: token }).then(....);
token.cancel(
... put here the information that will be available as the `err.message` parameter in the `cancel` interceptor
);
I'm using Angular2 on the client side and a node-express server as my backend. The node-server works as an API-middleware and also as my authentication service. The user-requests must contain a valid JWT token to perform requests on the node-server.
All of my GET functions and other PUT functions are working properly. I wrote a new one, which just should delete an ID on a third-party API, doesn't.
Furthermore, my node-express server sends custom error messages at some points to the client. This comes to my problem, whenever I run my latest PUT-function, my server responds with "No token provided". This happens when the user isn't logged in on the client side.
As I said, all my other functions working. this.createAuthenticationHeaders(); is necessary to perform valid request on the server side. But it's implemented.
In other words, the authentication gets lost between client and server and I get my own error message: "No token provided".
Appointment-Detail.Component.ts
cancelAppointment() {
this.authService.getProfile().subscribe(profile => {
this.username = profile.user.username; // Set username
this.email = profile.user.email; // Set e-mail
if (profile.user.email) {
this.apiService.cancelUserAppointment(this.id).subscribe(data => {
console.log(this.id);
if (!data.success) {
this.messageClass = 'alert alert-danger'; // Set error bootstrap class
this.message = data.message; // Set error message
} else {
this.messageClass = 'alert alert-success'; // Set success bootstrap class
this.message = data.message; // Set success message
// After two seconds, navigate back to blog page
}
});
}
});
}
API Service
cancelUserAppointment(id) {
this.createAuthenticationHeaders();
console.log('API SERVICE ' + id);
return this.http
.put(this.domain + 'api/appointments/' + id + '/cancel', this.options)
.map(res => res.json());
}
An API Service functions that works
getCertificatesByUser(email) {
this.createAuthenticationHeaders();
return this.http
.get(this.domain + 'api/user/' + email + '/certificates', this.options)
.map(res => res.json());
}
Server route to the third party API
router.put('/appointments/:id/cancel', (req, res) => {
console.log('hi');
var id = req.params.id;
const url = process.env.acuityUri + '/appointments/' + id + '/cancel';
console.log(id);
});
Authentication middleware
router.use((req, res, next) => {
const token = req.headers['authorization']; // Create token found in headers
// Check if token was found in headers
if (!token) {
res.json({
success: false,
message: 'No token provided'
}); // Return error
} else {
// Verify the token is valid
jwt.verify(token, config.secret, (err, decoded) => {
// Check if error is expired or invalid
if (err) {
res.json({
success: false,
message: 'Token invalid: ' + err
}); // Return error for token validation
} else {
req.decoded = decoded; // Create global variable to use in any request beyond
next(); // Exit middleware
}
});
}
});
Without doing too much of a deep dive into your auth headers, I see a pretty glaring issue that I think may be the cause of your troubles.
HTTP REST verbs carry different "intents", the intent we specifically care about in this case is wether or not your request should have a body.
GET requests do not carry a body with them.
PUT requests do carry a body.
Because of this, angular's HttpClient request methods (http.get, http.post, etc.) have different method signatures.
To cut to the chase, http.put's method signature accepts 3 parameters: url, body, and options, whereas http.get's method signature only accepts 2: url and options.
If you look at your example, for http.put you are providing this.httpOptions as the second parameter instead of the third, so Angular is packaging up your options object as the PUT request body. This is why you have a working example and a non-working example; the working example is a GET!
The solution? Simply put something else as the request body in the second parameter and shift this.options down to the third parameter slot. If you don't care what it is, just use the empty object: {}.
So your request should look like this:
return this.http
.put(this.domain + 'api/appointments/' + id + '/cancel', {}, this.options)
At the very least, this should send whatever is in this.options to the server correctly. Now wether what's in this.options is correct or not is another story.
Example PUT call from Angular's docs: https://angular.io/guide/http#making-a-put-request
I have login tied to Facebook authentication, and this is all handled by Firebase.
However I need to make an API call to Facebook 'me/friends/'
Since I am already logged in, how would I use OAuth object to make a call without making another request.
I am using following wrapper for Angular for connection to Facebook.
https://github.com/ccoenraets/OpenFB
You don't need a wrapper. $firebaseAuth() + $http() = easy Graph API requests.
The Graph API is pretty easy to use and will work easily with Firebase.
Make sure you have the Facebook Friends permission enabled or you won't get any data back.
You can use $firebaseAuth() to login and get the Facebook access_token. That token can be used against the Graph API to get data via HTTP requests. Angular has a good $http library for making these calls.
Don't mind the way I structure the code, I prefer to use the Angular styleguide.
angular.module('app', ['firebase'])
.constant('FirebaseUrl', 'https://<my-firebase-app>.firebaseio.com/')
.constant('FacebookAppId', '<app-id>')
.service('RootRef', ['FirebaseUrl', Firebase])
.factory('Auth', Auth)
.factory('Friends', Friends)
.controller('MainCtrl', MainCtrl);
function Friends($http, RootRef, $q, FacebookAppId) {
function getFriends() {
// get the currently logged in user (may be null)
var user = RootRef.getAuth();
var deferred = $q.defer();
var token = null;
var endpoint = "https://graph.facebook.com/me/friends?access_token="
// if there is no logged in user, call reject
// with the error and return the promise
if (!user) {
deferred.reject('error');
return deferred.promise;
} else {
// There is a user, get the token
token = user.facebook.accessToken;
// append the token onto the endpoint
endpoint = endpoint + token;
}
// Make the http call
$http.get(endpoint)
.then(function(data) {
deferred.resolve(data);
})
.catch(function(error) {
deferred.reject(error);
});
// return the promise
return deferred.promise;
}
return {
get: getFriends
};
}
Friends.$inject = ['$http', 'RootRef', '$q', 'FacebookAppId'];
function Auth($firebaseAuth, RootRef) {
return $firebaseAuth(RootRef);
}
Auth.$inject = ['FirebaseAuth', 'RootRef'];
function MainCtrl($scope, Friends) {
$scope.login = function login() {
Auth.$authWithOAuthPopup('facebook').then(function(authData) {
console.log(authData, 'logged in!');
});
};
$scope.getFriends = function getFriends() {
Friends.get()
.then(function(result) {
console.log(result.data);
});
};
}
MainCtrl.$inject = ['$scope', 'Friends'];
there is a way in Angular to force an http status code after requesting a particolar html page?
I want to force a 404 status when requesting a page with an particolar url for example: https://www.example.com/path/path?query=3.com
Not sure if you can retrieve the requested url int the response in an $http interceptor.
Try this:
myModule.factory('myHttpInterceptor', function ($q) {
return {
response: function (response) {
if (response.url === "http://test.com")
response.status = 404;
return response;
}
};
});
myModule.config(function ($httpProvider) {
$httpProvider.interceptors.push('myHttpInterceptor');
});
In my SPA I catch every 401 response from REST requests. From there I don't redirect to login page immediatly, but first I check to the backend if the problem is that the token has expired or not. If not (the user is not known) I redirect to login, but if it was an expired problem, I generate a new token then I run again the request that previously failed into a 401. Here is the code for my interceptor:
var $http, loginService;
return function (promise) {
return promise.then(function (response) {
return response;
}, function (response) {
if (response.status === 401) {
$http = $http || $injector.get('$http');
loginService = loginService || $injector.get('loginService');
var defer = $q.defer();
var promiseToken = defer.promise;
var configPreviousRequest = response.config;
console.log(configPreviousRequest);
var url = configurationService.serverUrl + "mobile" + configurationService.apiVersion + "/verify";
var request = $http.post(url, {'code': loginService.getVmmToken()});
// Get the token. If success, we try to login again
return request.then(
function (responseVerify) {
loginService.setVmmsToken(responseVerify.data);
loginService.setAuthentificationToken();
configPreviousRequest.headers.vmmsToken = responseVerify.data;
return $http(configPreviousRequest);
},
function () {
$location.path('/login');
});
}
return $q.reject(response);
});
};
But here is the result in Chrome Network tool. All methods are not in the correct number (called too many times for /verify and /blocks)
So I logged (with console.log(configPreviousRequest);) to see what happens. Here are logs:
We clearly observe that for one 401 error, I intercept it many times.
And I have no clue why :)
Has someone any idea?
Thanks