How to authorize only my app to use my REST API ?
I have this code in Javascript
$.ajax({
type: 'DELETE',
url : 'removeTest',
data: { ... },
beforeSend:function(){
...
},
complete:function(){
...
},
success:function(data, textStatus, jqXHR){
...
}
});
This call will remove a user from the database with REST API in PHP. The problem is that everyone can remove a user, with POSTMAN (Chrome plugin) for exemple. How can I protect my REST API to authorize only my app.
Check the HTTP_REFERER is not enough. What could be better ?
Thanks for your help
You have several possibilities here. In general you could authorize the user, the app or both. This depends on your application requirements.
Authenticate Applications
To authenticate the application you could use a token based system, such as an API-Key. This means any request would be signed using additional request parameters. Take a look at the way amazon does this in their S3 service for example. If your application will be the only one that will access the rest API you could alternatively simply restrict the acces by the IP address.
If there will be multiple users using the service via your client, you may also need to authorize the access to certain functions. For example: Will every user be allowed to delete any resource? If the answer is no, you have to implement
Authenticate and authorize users
A simple way to authenticate users in a RESTful API is using HTTP Basic or Digest Auth. In this setting the user credentials are sent via the Authorization header in a form of username:password as Base64 encoded hash to the server. Please note that you should only do this via an secured connection using HTTPS!
Additionally you could also take a look at more complex/sophisticated practices like OAuth.
Related
I have a single page create-react-app running on localhost:3000 and I want to log in to a laravel 7.x instance running on myapp.loc (vhost).
Eventually I would like a single page running on app.mysite.com with laravel running on api.mysite.com.
I'm able to log in to my laravel instance directly from myapp.loc. I've installed Laravel passport and the scaffolding, etc and can create Client IDs and Secrets, but I'm unsure if they are needed and if so, how to use them.
What I am unsure of and cannot find any documentation for, is how to log in to laraval from my SPA (running on localhost:3000). I have set my CORS headers and can connect requests that don't require auth, but I don't know how to log in or structure auth requests once logged in.
I can't find any documentation on axios.post / get requests with a focus on logging in from another domain and maintain user-based access.
Since I don't know enough to ask a concise question, there are three layers that I feel like I should be searching for an answer.
Is it possible for laravel to act as the backend for my single page app from another domain?
If so, are there documented, best practices / well worn paths for accomplishing this?
If so, what would a sample axios login and subsequent session call look like? (e.g. payload and header shape)
Yes you can, I suggest to use https://laravel.com/docs/7.x/sanctum instead of passport because is easier to setup and it was created especially for this scenario.
You need to configure CORS using the official Laravel Package https://github.com/fruitcake/laravel-cors this way you will open Laravel's CORS to be able to reach it from anywhere localhost, or any domain that you can set up into allowed_origins. inside the cors.php config file according to the documentation of the package.
After configuring Sanctum/Passport and ensuring you are generating the required token for your SPA using the createToken method described in Sanctum or Passport docs, you have to save the token to connect to your protected endpoints however they recommend to use cookie SPA based authentication but that's not strictly necessary.
Create an API Service
In this example I will create an API Service to encapsulate API calls
import axios from 'axios';
const URI = 'https://yourlaravel.api/api/';
axios.defaults.headers.common = { Accept: 'application/json', 'Content-Type': 'application/json' };
const ApiInstance = axios.create();
const API = {
login: (user) => {
return ApiInstance.post(`${URI}login`, user);
},
getUser: () => {
return ApiInstance.get(`${URI}user`);
},
setUser: (user) => {
return ApiInstance.post(`${URI}user`, user);
},
};
Send A Login Request to your login endpoint and save the token
import API;
API.login({email:'mail#domain.com',password:'32323'})
.then(response=>{
//save the token
//response.data.accessToken
})
Fetch data from your protected endpoints using the token
//Set the default authorization header when you have an access token
axios.defaults.headers.common = {'Authorization': `Bearer ${token}`}
//Get your data
API.getUser().then(response=>{
//response.data
})
//Post something
API.setUser({user:'Os'}).then(response=>{
//response.data
})
All those things are possible, you just need to set up cors and you are good to go. For auth you can use passport or your own custom app key setup, it all depends on what you are trying to achieve. I suggest reading up about RESTfull apis, that would be a good start.
In order to perform a handshake between FE and BE on FE you would have a login form submission of which will send e request to BE (backend api) and if login is success you send back a key which then FE should store. Any future requests from FE should append that key in the header to gain access to authorised areas.
There should be plenty of information on this subject (RESTfull Api & Token authentication) on google.
My app creates a custom attribute "userType" for each new signed-up user. Now I would like this "userType" claim/attribute to be added to the JWT access token whenever the user signs in or the token gets refreshed.
Is there an option to tell cognito to add my custom claim/attribute to the JWT access token? (Without a pre token generation Lambda)
Custom attributes are not available in Cognito access token. Currently it is not possible to inject additional claims in Access Token using Pre Token Generation Lambda Trigger as well. PreToken Generation Lambda Trigger allows you to customize identity token(Id Token) claims only.
You can use ID token to get the token with custom attributes.
Access tokens are not intended to carry information about the user. They simply allow access to certain defined server resources.
You can pass an ID Token around different components of your client, and these components can use the ID Token to confirm that the user is authenticated and also to retrieve information about them.
How to retrieve Id token using amazon cognito identity js
cognitoUser.authenticateUser(authenticationDetails,{
onSuccess: function(result) {
var accessToken = result.getIdToken().getJwtToken();
console.log('accessToken is: ' + accessToken);
},
onFailure: function(err) {
alert(err.message || JSON.stringify(err));
},
});
I have the same problem when I want to create several microservice. There isn't a way I can customize an access token, but only an identity token. However, I use client credentials in the machine-to-machine which needs access token. So, in no way I can customize my token. At last, I decide to add such info(like user type) in the event header. It's not a very secure way compared to customize a token, but there isn't any other easy way to do it right now. Otherwise, I have to rewrite the authorizer in Cognito. Like rewriting a customize authorizer and it's very painful.
I have the same issue with Cognito; exist other tools like "PingFederate"Auth-server of Ping identity and Auth0 Auth-server; I know that the requirement isn't part of the standard, but these applications were my alternatives to fix this issue
The responses suggesting to use the ID Token for authorization in your backend systems are bad security practice. ID Tokens are for determining that the user is in fact logged in and the identity of that user. This is something that should be performed in your frontend. Access Tokens on the other hand are for determining that a request (to your backend) is authorized. ID Tokens do not have the same security controls against spoofing that Access Tokens have (see this blog from Auth0: https://auth0.com/blog/id-token-access-token-what-is-the-difference/).
Instead, I recommend that your backend accept an Access Token as a Bearer token via the Authorization HTTP header. Your backend then calls the corresponding /userinfo endpoint (see: https://openid.net/specs/openid-connect-core-1_0.html#UserInfo) on the authorization server that issued the Access Token, passing such said Access Token to that endpoint. This endpoint will return all of the ID Token information and claims, which you can then use to make authorization decisions in your code.
Suppose I make an API request to the following URL:
https://myapi.com/data
This is an API I built and have full control over.
I would like to limit access to this API to only the apps I authorize.
I see many services will provide you with an API key which you can append to your URL to give you access.
Suppose I have:
https://myapi.com/data?key=a6reallly7long2string9of0numbers2and4letters
Then on the backend I have something like:
class REST {
public $ACCESS_TOKEN = 'a6reallly7long2string9of0numbers2and4letters',
public function Auth($token){
if($token===$this->ACCESS_TOKEN) return true;
return false;
}
}
If the values match, I allow access.
But all someone would have to do is look at the request the app is making on the client side and they have the token.
Even if I encrypt the token or use one-way hashing, they'll still have the value that decrypts to the correct result.
How does one approach good authentication via URL token for an API?
I would like to limit access to this API to only the apps I authorize.
What are you looking for is "access authorization". Indeed, an access token seems to be a good way, but there are some aspects missing
** Authorization header **
By default the token should be sent as an HTTP header (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization) not in the url
Commonly used Authorization headers types are Basic (for basic authentication) and Bearer (for OAuth authentication and authorization)
The token should not be a hardcoded constant. It should be created/generated based on the application (and optionally user) authentication. And still better if the token is temporary
Now you can ask - how can an application keep its credentials secret? Each application can have their own server services (end user should.not access application credentials) or pure web application should be comtrolled by the CORS headers
Just search for OAuth 2.0 protocol and JWT token (jwt should be self-contained and signed).
IMHO the URL token may be an option when there is no other alternative, as URL is often cached, resent, logged,...
** API Manager **
If you have resources (server) to do so, you can deploy an API manager (there are open source, commercial or cloud options, just search for some). API manager will handle the application enrollment, authorization and enforcement.
Using these information
https://developer.spotify.com/web-api/authorization-guide
I am trying to implement the 'Client Credentials Flow' to automate the authentication process. But somehow I got the error below.
XMLHttpRequest cannot load https://accounts.spotify.com/api/token?grant_type=client_credentials. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'websiteURL' is therefore not allowed access.
$http.get('https://accounts.spotify.com/api/token?grant_type=client_credentials', {
headers: {
'Authorization': 'Basic ' + key)
}
}).success(function(r) {
console.log('got access token', r);
}).error(function(err) {
console.log('failed to get access token', err);
});
I have also tried it via Ajax / Jquery and other methods but still have this one 'Access-Control-Allow-Origin'.
I have read a lot of stuffs regarding this about CORS, Angular, Node, etc. And honestly I'm confused which of these should I use and I don't have an idea how to.
Can someone simplify this for me please?
This is not the flow you're looking for - The Client Credential flow is meant for communication between servers.
Instead, have a look at the Authorization Code Flow, and why not Jose Perez's excellent JavaScript wrapper for front-ends working with Spotify's Web API.
This flow allows your application's users to authenticate and authorize your application to act on their behalf (e.g. add tracks to playlists, create playlists, and so forth). Access tokens retrieved using the Client Credentials flow isn't connected to a specific user, which limits what your application can do using it.
We are building system that required login information for all pages. the application is designed to be Restful application using codeigniter as Phil Sturgeon library. This library is using API Key only to authorize api calls via sending it with every request over HTTPS connection.
Even if it using 2 way authentication or only API Key. What i am searching for a while is the following scenario:
User request the application for the first time (ex: https://www.xyz.com) then it will be redirected to the login page to check credentials
User enter the usernam/password and sent it via POST over the https
Server check if the information is valid then:
API KEY should be provided by the server to the client as a resource identified by this username (Here is the question???!!!)
How to send the API Key to the client in a secure way?
1) Could i use session-cookies and restore the API KEY in a cookie then use this API KEY on every coming request (This is violent the Stateless of the Rest and i don't sure if it securely enough).
2) Actually i don't know other options :) it's your turn if you could help
If you could give an example it would be a great help as i found and read lots of articles
:)
Since the connection is HTTPS, anything you send over the wire is secure (theoretically and provided you aren't being mitm'd). Not sure if the whole API is served over HTTPS (you didn't specify), so even though you could return the key as part of the login (while still under the umbrella of HTTPS), if the rest of the api isn't HTTPS, the key could be sniffed on the next request.
Sessions and cookies aren't typically part of a RESTful application; REST is stateless.
Something like a revolving key would be better for non-HTTPS (would work with HTTPS too). You login via HTTPS, server returns the api key, you use it on the next request, server returns new api key, you use it on the next request and so on. While it's better than a single api key over non-HTTPS, it's not perfect. If someone sniffs the response from one of the subsequent requests and you don't end up consuming that key, they can use it. This shrinks the attack vector to a non-HTTPS response from server to client since if a request from client to server is sniffed, the api key will have already been consumed by your legitimate request. However, more should be done to secure the api if you aren't serving it over HTTPS.
If it were me, I'd look into request signing + https. There's some talk of request signing here: https://stackoverflow.com/a/8567909/183254
There's also some info on digest auth at the Securing the API section of http://net.tutsplus.com/tutorials/php/working-with-restful-services-in-codeigniter-2/
A pseudo-code example js function on the client
function get_calendar(){
var key = $('#api_key').value();
$.ajax({
type: 'get',
url: '/index.php/api/calendar?key=' + key,
success: function(response){
// show calendar
// ...
// set received api key in hidden field with id api_key
$('#api_key').value(response.api_key)
}
})
}
Example controller method:
function calendar_get($api_key = ''){
if($api_key_matches){//verify incoming api key
$r = array();
$r['calendar'] = $this->some_model->get_calendar();
$r['api_key'] = $this->_generate_api_key();// generate or get api key
}
$this->response($r);
}