Angular and digest auth - javascript

I use passport.js with my node.js server.
I have an RESTFull api and want to implement digest auth so that my user can then interact with my API.
In Angular, using $resource, how can I implement the digest auth process? I see that using username, realm and password I must compute a "nonce" using MD5...
If you already did that, I would be happy to learn.

your can put the token in the http header if user login success.
In angularjs you can do this:
//put the user token to the default http request headers after user login.
$httpProvider.defaults.headers.get['API-Token'] = 'vy4eUCqpQmGoeWsnHKwCQw';
//create service and push the token to the request headers.
angular.module('usersService', ['ngResource'])
.factory('User', function($resource, api-token) {
var User = $resource('http://api.test.com\\:8080/1.0/users', { }, {
query: {
method: 'GET',
isArray: true,
headers: { 'API-Token': api-token }
}
});
return User
});

Related

Laravel 5.5 Passport client_secret and Vue JS Auth

Good Day everyone,
I'm learning Laravel Passport and Vue.JS(standalone) at the same time.
I'm using Password Grant Token to authenticate the user.
I encounter this issue that the secret_key must be always hidden.
I have this Login Component in my vuejs where I need to add the client_secret as parameter to get access token. However, since VUEJS is a javascript framework. there's a way that someone can see the client_secret on the minified build file.
my question is that, is it just normal? is there a way to conceal the client_secret?
at first I don't mind the issue since I have implemented CORS on laravel where I can only select the allowedOrigins. My thinking is that it doesn't matter if they know the secret key as long as I can filter the allowedOrigins.
Here's my code in VUEJS
login(){
this.$validator.validateAll().then((result) => {
if (result) {
var data = {
client_id: 3,
client_secret: 'client-secret key',
grant_type: 'password',
username: this.inputs.email,
password: this.inputs.password
}
this.$http.post("oauth/token", data).then(response => {
this.$auth.setToken(response.body.access_token, response.body.expires_in + Date.now());
bus.$emit('reload');
this.$router.push('/');
})
}
});
}
Any advice will be appreciated.
Thanks in advance.
Laravel Passport has a built in way of allowing you to consume your own API with a Javascript application. It provides a simple middleware which you can add to your web middleware group (which you'll find in App\Http\Kernel):
'web' => [
// Other middleware...
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
Laravel will check to see if you have a logged in user (via the standard cookie/session guard) and if so it will generate JWT for you and store it in a cookie. It will also check for this cookie's presence and validate it when you make requests to your API so you no longer need to pass an access token.
One thing to note however, is that you will need to ensure that you continue to pass your CSRF tokens with your requests (assuming you have CSRF protection turned on). If you're using Axios with Vue, you can make sure this happens very easily with the following:
window.axios.defaults.headers.common = {
'X-Requested-With': 'XMLHttpRequest',
};
With this approach you don't need to worry about access tokens at all or expose a client_id and secret to the client.
I faced the same problem and found an interesting solution.
You can add a custom endpoint on the backend and make the request from there.
All you have to do is to:
First, create a route in the api.php file Route::post('/login', 'AuthController#login');
Then, create the AuthController and login function associated with that route php artisan make:controller AuthController
Finally, install Guzzle, the HTTP client that will allow you to make a request from PHP composer require guzzlehttp/guzzle and make the request from the login function
public function login(Request $request)
{
$http = new \GuzzleHttp\Client;
try {
$response = $http->post('http://example.test/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => 2,
'client_secret' => 'your_client_secret',
'username' => $request->username,
'password' => $request->password,
]
]);
return $response->getBody();
} catch (\GuzzleHttp\Exception\BadResponseException $e) {
if($e->getCode() == 400)
{
return response()->json('Invalid Request, Please enter email or password.', $e->getCode());
}
else if($e->getCode() == 401)
{
return response()->json('Your credentials are incorrect. Please try again', $e->getCode());
}
return response()->json('Something went wrong on the server.', $e->getCode());
}
}
Now, the vue.js front end app juste needs to send a post request to http://example.test/login with the username and password to get back the access_token without knowing the client_secret since it is abstracted to the backend.
Here is the video that explains it and implements it really well.
And a presentation about some theory and how you can store and send the token from the vue.js app once you retrieve the token.
Hope this helps.

Unable to query Google Search Console API using a Service Account

I need to retrieve some data from Google Search Console (Webmaster Tools) using a service account.
So far I've been able to retrieve an access_token for the service account which I need to append to the url of the request. The problem is that I can't find a way to do so, this is the code i'm using:
function retrieveSearchesByQuery(token)
{
gapi.client.webmasters.searchanalytics.query(
{
'access_token': token,
'siteUrl': 'http://www.WEBSITE.com',
'fields': 'responseAggregationType,rows',
'resource': {
'startDate': formatDate(cSDate),
'endDate': formatDate(cEDate),
'dimensions': [
'date'
]
}
})
.then(function(response) {
console.log(response);
})
.then(null, function(err) {
console.log(err);
});
}
This is the url called by the function:
https://content.googleapis.com/webmasters/v3/sites/http%3A%2F%2Fwww.WEBSITE.com/searchAnalytics/query?fields=responseAggregationType%2Crows&alt=json"
Instead it should be something like this:
https://content.googleapis.com/webmasters/v3/sites/http%3A%2F%2Fwww.WEBSITE.com/searchAnalytics/query?fields=responseAggregationType%2Crows&alt=json&access_token=XXX"
The gapi.client.webmasters.searchanalytics.query doesn't recognize 'access_token' as a valid key thus it doesn't append it to the url and that's why I get a 401 Unauthorized as response.
If I use 'key' instead of 'access_token' the parameter gets appended to the url but 'key' is used for OAuth2 authentication so the service account token I pass is not valid.
Does anyone have a solution or a workaround for this?
If your application requests private data, the request must be authorized by an authenticated user who has access to that data. As specified in the documentation of the Search Console API, your application must use OAuth 2.0 to authorize requests. No other authorization protocols are supported.
If you application is correctly configured, when using the Google API, an authenticated request looks exactly like an unauthenticated request. As stated in the documentation, if the application has received an OAuth 2.0 token, the JavaScript client library includes it in the request automatically.
You're mentioning that you have retrieved an access_token, if correctly received, the API client will automatically send this token for you, you don't have to append it yourself.
A very basic workflow to authenticate and once authenticated, send a request would looks like the following code. The Search Console API can use the following scopes: https://www.googleapis.com/auth/webmasters and https://www.googleapis.com/auth/webmasters.readonly.
var clientId = 'YOUR CLIENT ID';
var apiKey = 'YOUR API KEY';
var scopes = 'https://www.googleapis.com/auth/webmasters';
function auth() {
// Set the API key.
gapi.client.setApiKey(apiKey);
// Start the auth process using our client ID & the required scopes.
gapi.auth2.init({
client_id: clientId,
scope: scopes
})
.then(function () {
// We're authenticated, let's go...
// Load the webmasters API, then query the API
gapi.client.load('webmasters', 'v3')
.then(retrieveSearchesByQuery);
});
}
// Load the API client and auth library
gapi.load('client:auth2', auth);
At this point, your retrieveSearchesByQuery function will need to be modified since it doesn't need to get a token by argument anymore in order to pass it in the query. The JavaScript client library should include it in the request automatically.
You can also use the API Explorer to check what parameters are supported for a specific query and check the associated request.
If you need to use an externally generated access token, which should be the case with a Service Account, you need to use the gapi.auth.setToken method to sets the OAuth 2.0 token object yourself for the application:
gapi.auth.setToken(token_Object);

No authorization token was found when res.redirect

I have two applications, both on Nodejs. One front-end and other back-end.
My back-end app is protected with token access using express-jwt and jsonwebtoken middlewares.
My problem is: I am making a request from front-end to back-end passing the token on header, back-end accepts the request and respond properly. Then in the front-end I redirect the response to an specific page (res.redirect('/')), in that moment I get the error UnauthorizedError: No authorization token was found
My front-end request:
/* Authentication */
router.post('/', function(req, res, next) {
// request login service
request({
uri: env.getUrl() + "/user",
method: 'POST',
timeout: 10000,
headers: {
'Authorization': 'Bearer '.concat(global.token)
},
form: { login : req.body.login, pwd : req.body.pwd }
}, function(error, response, body){
if(error) {
logger.error(error);
res.render("error", {message: "Error getting user" });
}
else {
if(body){
req.session.usuario = JSON.parse(body);
res.redirect("/");
} else {
res.render("login", {message: "Login Failed" });
}
}
});
});
I don't know why this happen. Could you help me?
Thanks in advance.
A redirect (via res.redirect) issues a new HTTP request. This means that the Authorization header is empty. This results in the UnauthorizedError error.
To fix this, you have two options:
1. Pass the token in the URI
You can issue the redirect with the token passed in the URL in this way:
res.redirect("/?access_token=" + global.token);
2. Set the header before the redirect
You can set the 'Authorization' header before making the redirect request:
req.session.access_token = global.token;
Problem found.
Anytime the my front-end app makes a request to the back-end side (api) the user logged in front-end is validated against back-end and so the fron-end's session is updated as well. Which means that every request is actually two requests:
One as the real request the app is doing.
The request validating the user logged on front-end in order to be sure that user exists.
This update (second point) was made without providing a token.

keycloak javascript adapter request offline token

I have js application for which I use keycloak 1.7-final JavaScript adapter with public client and standard flow and it works fine but now I wanted to add offline capability to the application and don't know how to get offline token. according to the documentation the client can request offline token by adding parameter "scope=offline_access" when sending authorization request to Keycloak.
The thing is I don't know where to add the parameter. I tried to add to "keycloak.config" and "keycloak.init" but its not working.
in keycloak.config:
keycloak: {
url: 'link omitted',
realm: 'sisdn-realm',
clientId: 'sisdn',
redirect_uri: 'link omitted',
flow: 'standard',
'enable-cors': true,
scope: 'offline_access'
}
and in keycloak.init
const keycloakConf = require('keycloak')//this is the same config above
var keycloak = new keycloakConf(config.keycloak)
keycloak.init({
onLoad: 'login-required',
checkLoginIframe: false,
scope: 'offline_access'
})
.success(authenticated => {
if(!authenticated)
dispatch(loginFailure())
else {
global.keycloak = keycloak
dispatch(loginSuccess())
}
})
From the JS adapter code:
The init method doesn't accept the scope option, but the login method does.

AngularJS - Handling refresh token?

I'm building a SPA with AngularJS with communication to a service (JAVA).
When user sends his username/pass, service sends back both: Acces token and Refresh token. I'm trying to handle: if I get response with status 401, send back refresh token and then send your last request again. I tried to do that with including $http, but angular doesn't let me include it in this interceptor. Is there any way to recreate the original request with this response parameter I'm recieving?
Something like:
I get 401
save my request
if I have a refresh token send that refresh token
on success resend my request
on error redirect to /login page
'use strict';
angular.module('testApp')
.factory('authentificationFactory', function($rootScope, $q, $window, $location, CONF) {
return {
request: function(config) {
config.headers = config.headers || {};
if ($window.sessionStorage.token) {
config.headers.Authorization = 'Bearer ' + $window.sessionStorage.token;
}
console.log(config);
$rootScope.lastRequest = config;
return config;
},
response: function(response) {
console.log($rootScope.lastRequest);
if (response.status === 401) {
if ($window.sessionStorage.refreshToken) {
//Save, request new token, send old response
//if it fails, go to login
$location.url('/login');
} else {
$location.url('/login');
}
}
return response || $q.when(response);
}
};
});
Bonus Question (the main question is more important): There are 2 mobile apps that will also connect to my service, and when I log in from my web app, and few moments later from my mobile app, mobile app takes a new refresh token and my web app's refresh token is valid no more. What would be the best option for dealing with that?
Thank you for your time,
Best regards
Have a look at this: https://github.com/witoldsz/angular-http-auth.
He uses a buffer to replay the requests after authentication.
I would strongly advise against sending and storing refresh tokens on SPAs like Angular.
If you are using session storage or local storage, you are opening a window of opportunity for the this refreshToken to be captured, either by a XSS attack, or by the user leaving the computer unattended.
See this article or this question for more info.

Categories