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.
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.
I'm currently using passport.js with the strategy from passport-google-oauth20 and I want to implement incremental authorization. I'm using yarn as my package manager and my server also uses the Nodejs and Express packages.
When a user first logs in, all I need is their basic gmail information like their email address, so I specify the "email" scope:
router.get('/',
passport.authenticate('google', {
hd: '(my domain)',
scope: ['email'],
accessType: 'offline',
prompt: 'consent'
}));
When they are on a different page, they can request that the site send an email from their account so I now need to add the "https://www.mail.google.com" scope to the session. I couldn't find any documentation for it on the passport.js website and google has a page on it, but I don't think that's setup since I'm using passport.js. I've tried using
router.post('/sendEmail',
passport.authenticate('google', {
hd: '(my domain)',
scope: ['https://www.mail.google.com'],
accessType: 'offline',
include_granted_scopes: true,
prompt: 'consent',
failerRedirect: '/auth',
successRedirect: '/google/authorizedSendEmail'
}));
but I get the error
Failed to load https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&prompt=consent&hd=(myDomain)&response_type=code&redirect_uri=(myCallbackURL)&scope=https%3A%2F%2Fwww.mail.google.com%2Fauth%2Fuserinfo.email&client_id=(usersID).apps.googleusercontent.com:
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin '(my domain)' is therefore not allowed access.
when requesting it.
If I include "https://www.mail.google.com" in the initial scope and don't have the second authentication call, then it does work; an email is sent from the user's account. But, of course that isn't incremental authorization.
Also, I think incremental authorization should have its own tag, but I don't have the rep to do so, could someone add it please?
I'm trying to make a sock.js connection from the frontend to the vertx backend.
my initial try looked like this:
let token = '<the token>';
let data = {'Authorization' : 'Bearer ' + token};
let eb = new EventBus("http://localhost:8080/eventbus");
eb.onopen = function () {
eb.registerHandler('notifications', data, (err, msg) => {
// handle the response
});
}
this doesn't work since I need to send the auth data on EventBus creation, even though the official sock.js documentation states that this is not supported. Obviously now sending new EventBus("http://localhost:9090/eventbus", data) doesn't work either.
https://github.com/sockjs/sockjs-node#authorisation
my backend handler for this:
final BridgeOptions bridgeOptions = new BridgeOptions()
.addOutboundPermitted(new PermittedOptions().setAddress("notifications"))
final SockJSHandler sockJSHandler = SockJSHandler.create(vertx).bridge(bridgeOptions, event -> {
event.complete(true);
});
router.route("/eventbus/*").handler(ctx -> {
String token = ctx.request().getHeader("Authorization"); // null
});
router.route("/eventbus/*").handler(sockJSHandler);
whatever I tried the header field Authroization is always null.
What is the standard way to authenticate the sock.js connection and register to an eventbus request in vertx?
SockJS uses WebSockets by default. You can't add custom headers (Authorization, etc) using JavaScript WebSocket API. Read this thread for more explanation.
I see 2 ways, how you can add authorization:
Just add token parameter to URL:
let eb = new EventBus("http://localhost:8080/eventbus?token=" + token);
and here's how you can get it on a server:
String token = ctx.request().getParam("token");
Send authorization message after connecting to the server. It can be some JSON object, which contains token field.
I think, 1st option is enough, however, 2nd one can be harder to implement in terms of Event Bus and SockJS.
Since sending Authorization header is not possible, attaching a token query parameter (as described by #berserkk) is the way to go.
However, in some circumstances, it may be undesirable to send your main login token in plain text as a query parameter because it is more opaque than using a header and will end up being logged whoknowswhere. If this raises security concerns for you, an alternative is to use a secondary JWT token just for the web socket stuff.
Create a REST endpoint for generating this JWT, which can of course only be accessed by users authenticated with your primary login token (transmitted via header). The web socket JWT can be configured differently than your login token, e.g. with a shorter timeout, so it's safer to send around as query param of your upgrade request.
Create a separate JwtAuthHandler for the same route you register the SockJS eventbusHandler on. Make sure your auth handler is registered first, so you can check the web socket token against your database (the JWT should be somehow linked to your user in the backend).
I think best way to secure a web-socket is using CORS check
Cross Origin Resource Sharing is a safe mechanism for allowing resources to be requested
router.route().handler(CorsHandler.create(your host origin path).allowCredentials(true));
We can add more layer of security also using sockjs :
Allow events for the designated addresses in/out of the event bus bridge
BridgeOptions opts = new BridgeOptions()
.addInboundPermitted(new PermittedOptions().setAddressRegex(Constants.INBOUND_REGEXP));
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.
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.