AngularJS and REST resources naming wondering - javascript

So in my angular js app in service called 'authService' I have the following resources:
var userAreaLogin = $resource('/api/user_area/login');
var userAreaSignup = $resource('/api/user_area/signup');
var session = $resource('/api/user_area/getSession');
var userAreaLogout = $resource('/api/user_area/logout');
but this doesn't feel quite right, I'm using only the get methods, for example:
this.login = function(credentials) {
var user = userAreaLogin.get(credentials, function() {
...
});
};
this.signup = function(userInfo) {
var signup = userAreaSignup.get(userInfo, function() {
...
});
};
I'm confused about what resources to use, should I have something like this?
var session = $resource('/api/user/session');
var userArea = $resource('/api/user');
userArea.get(credentials); //should login the user?
userArea.post(credentials); //should signup the user?
session.delete(); //should logout the user?
session.get(); //should get the sessions of the logged user if any?

By REST sessions are maintained by the client and not by the service. You should use HTTPS and send the username and password with every request (for example with HTTP basic auth headers) instead of using session cookies... (stateless constraint)
Ofc. on the client side you can have login and logout which will change the content of the auth headers sent via AJAX.

You are going to the right direction.
In a well designed REST API you should have something like this.
POST /users/sign_in # Create a user session (signs in)
DELETE /users/sign_out # Delete a user session (signs out)
POST /users # Create a new user resource
GET /users/:id # Get the user resource
Based on these API you can then define your services. I also suggest to use $http which is cleaner, although you'll write few lines of code more.
# Session related methods
Session.create
Session.delete
# User related methods
User.create
User.get
Hope this makes things clearer.

Related

Unity and Google OAuth2 Authorization Code Flow

I am trying to build a Unity application to be deployed with WebGL. I am trying to incorporate Google Sign-In into the application, and so far, this was what I've managed to make work in the Unity WebGL build in Chrome:
User presses on the "Login with Google" button on Unity application, in Tab A.
User is directed to Google Sign In page on another Tab B.
User signs in with Google account, and is redirected to my redirect_uri, which is simply https://localhost, with the auth code parameter.
My question is, is it possible for me to do the following, possible with .jslib files:
Instead of going to redirect_uri on Tab B, instead go back to Tab A without reloading, passing along the auth code.
Building on the line above, have javascript handlers, that:
When auth code is received, initiate request to exchange auth code for the id_token as instructed here.
When id_token is received, call a C# Script function to do further actions with the id_token.
Alternatively, I can set redirect_uri to be an endpoint on my backend server, and perform the auth token -> id_token flow using the Google client SDKs. However, for this approach, I would like to know if i am able to
After the auth token -> id_token flow is completed on the backend server, close the current window, Tab B, and go back to Tab A.
After we’re back on Tab A, redirect Unity to a specific scene (not the login scene anymore, but a home page that users are directed to after they are authenticated).
Would very much appreciate any help i can get :')
EDIT: For better clarity, what I want to achieve is something that FacebookSDK for Unity has done in their FB.LogInWithReadPermissions(). The whole auth code -> access_token flow is seamless, and i get redirected back to the Unity application in Tab A at the end with the access_token.
I managed to find a Javascript solution to achieve my first method. The differences are that because
My application will never be in production
Consistency with my Facebook OAuth implementation,
I used the implicit flow instead of the authorization code flow, despite it being not the recommended way due to security concerns. However, I think you can easily use the authorization code flow, retrieving the authorization code and passing it on to your backend to exchange for an id token. (as far as I know, you cannot use Javascript/XHR requests to do this exchange)
So, the flow is that from my C# script, I call a Javascript function from a .jslib file. Basically, the function detects when the OAuth window has redirected back to my redirect_uri, then gets the access_token parameter from the redirected URI, and calls a C# Script function. From there, you should be able to do whatever you need to do (change scene, send to your backend, etc.). Note that there is a try/catch because there will be errors if you attempt to get information from the Google Sign In pages.
The file is as follows:
mergeInto(LibraryManager.library, {
OpenOAuthInExternalTab: function (url, callback) {
var urlString = Pointer_stringify(url);
var callbackString = Pointer_stringify(callback);
var child = window.open(urlString, "_blank");
var interval = setInterval(function() {
try {
// When redirected back to redirect_uri
if (child.location.hostname === location.hostname) {
clearInterval(interval) // Stop Interval
// // Auth Code Flow -- Not used due to relative complexity
// const urlParams = new URLSearchParams(child.location.search);
// const authCode = urlParams.get('code');
// console.log("Auth Code: " + authCode.toString());
// console.log("Callback: " + callbackString);
// window.unityInstance.SendMessage('Auth', callbackString, authCode);
// Implicit Flow
var fragmentString = child.location.hash.substr(1);
var fragment = {};
var fragmentItemStrings = fragmentString.split('&');
for (var i in fragmentItemStrings) {
var fragmentItem = fragmentItemStrings[i].split('=');
if (fragmentItem.length !== 2) {
continue;
}
fragment[fragmentItem[0]] = fragmentItem[1];
}
var accessToken = fragment['access_token'] || '';
console.log("access_token: " + accessToken);
child.close();
// Invoke callback function
window.unityInstance.SendMessage('Auth', callbackString, accessToken);l
}
}
catch(e) {
// Child window in another domain
console.log("Still logging in ...");
}
}, 50);
}
});
Then, in my C# script, I call this function using the following:
public class GoogleHelper : MonoBehaviour
{
[DllImport("__Internal")]
private static extern void OpenOAuthInExternalTab(string url, string callbackFunctionName);
// ...
public void Login(string callbackFunctionName) {
var redirectUri = "https://localhost";
var url = "https://accounts.google.com/o/oauth2/v2/auth"
+ $"?client_id={clientId}"
+ "&response_type=token"
+ "&scope=openid%20email%20profile"
+ $"&redirect_uri={redirectUri}";
OpenOAuthInExternalTab(url, callbackFunctionName);
}
// ...
}
Of course, this is super hacky, and I'm not very familiar with Javascript and so don't really know the implication of the code above, but it works for my use case.

How to prevent user from accessing JSON data from query parameters?

I am developing a really simple webapp that searches for a company's stocks.
Here is the JS code (uses AJAX to fetch the company's stock from the server):
document.getElementById("requestQuoteBtn").addEventListener("click", function createQuoteRequest(){
var quoteSymbol = document.getElementById("requestedSymbol").value;
var quoteRequest = createAJAX();
quoteRequest.open('GET', '/quote?sym='+quoteSymbol);
quoteRequest.send();
quoteRequest.onload = function getQuoteRequest(){
if(quoteRequest.status == 200){ // SUCCESSFUL
displayQuoteData(false, JSON.parse(quoteRequest.response)); // basically shows a hidden div with the data
}
else // NO COMPANY W/ THIS SYMBOL FOUND
displayQuoteData(true, null);
};
});
Here is the Flask code:
#app.route("/quote", methods=["GET"])
#login_required
def quote():
requestedSymbol = request.args.get("sym")
if not requestedSymbol:
return "no symbol"
quoteData = lookup(requestedSymbol) # USES AN API TO FETCH COMPANY'S STOCK
if quoteData is None:
return "NONE", 404
else:
return quoteData
The issue is that if the user accesses, for example, this URL:
www.mywebsite.com/quote?sym=AAPL
It will literally show a raw HTML with JSON's data, instead of my website with the data:
{"name":"Apple, Inc.","price":"$245.18","symbol":"AAPL"}
How can I prevent this?
If you simply want to make sure that users do not accidentally access your api endpoint when trying to access your website (aka this is about user experience and your not concerned with adding auth to your API endpoint)
The easiest way is to create separate routes for your api and your client routing
Update:
#app.route("/api/quote", methods=["GET"])
Likewise update:
quoteRequest.open('GET', '/api/quote?sym='+quoteSymbol);
Your client routing will still be:
#app.route("/quote", methods=["GET"])
If you want to make sure that nobody can access your api endpoint then you need to add some sort of authorization to your endpoint.
If you do not secure your API with some authorization then anyone can access the data you return from your server API simply by visiting the route.
Either way setting up separate routes for your API endpoints and client side routes should solve the problem of showing API data instead of your client template when visiting:
mywebsite.com/quote?sym=AAPL

AngularJs bookmarkable url and query parameters

I'm trying to fix one mistake which one of the previous developer has did. Currently in project we have half-bookmarkable pages. Why half? Because if token has expired user will be redirect to resourse where he has to provide his credentials and on success he will be redirect to previous resource which he has used before. Sounds good for now. The problem here is next, some of resources have server side filtering and highly complicated logic which comes in the end to the one object like this:
param = {
limit: 10,
offset: 0,
orderBy: 'value',
query: 'string query can include 10 more filters'
};
then thru the service it sends a request to the end point
var custom = {};
function data(param) {
custom.params = param;
return $http.get('/data_endpoint', custom)
.then(function (response) {
return response.data;
});
From this part request works fine and response is correct but url isn't. User cannot store current url with search params because url doesn't have it and if user will try to get back to previous page with applied filters he won't be able to do that.
Also there is interceptor in this application, which add one more query param to every api request, because every api request requires some specific validation. This param is always the same.
I have tried to add $location.search(param) exactly to this data service but it breaks the app with infinity loop of login / logout looping, because interceptor stops working and all other request are sending without special query param
Then I thought to add this params inside interceptor like this:
config.params.hasOwnProperty('query') ? $location.search(config.params) : $location.search();
But it still breaks app to infinity loop.
This method adds query to url but doesn't allow to modify it, so if user applied additional filtering, it doesn't send new modified request but sends old one.
if (config.params.hasOwnProperty('query')){
for (var key in config.params){
$location.search(key, config.params[key])
}
}
That's why I decided to ask question here, probably somebody gives an advice how to solve this problem. Thanks in advance.

javascript - make facebook page post

I am not using the Javascript SDK because that is client-side whereas I'm making a server-side call.
I want to make a page post so that I can make an ad creative with it. I can do the call perfectly fine in the Graph API Explorer tool, but I cannot make the same call (with the same long-lived access tokens that continue to work in the Graph Explorer) from Javascript. Here is my code:
tok = <valid and never expiring user token>;
var pg_tok = <valid and never expiring page token>;
var act_id = <account_id>;
var pg_id = <page_id>;
var call_to_action = 'INSTALL_MOBILE_APP';
var fb_app_url = 'https://itunes.apple.com/us/app/id284882215';
var msg = 'Test creative, ya see';
var pic_url = 'https://s3.amazonaws.com/<path_to_my_image>';
var ROOT = 'https://graph.facebook.com/';
var pagepost_endpoint = ROOT+pg_id+'/feed';
console.log(pagepost_endpoint);
var pagepost_params = {
access_token: pg_tok,
call_to_action: {
type: call_to_action,
value: {link: fb_app_url}
},
message: msg,
picture: pic_url,
published: false
};
console.log(pagepost_params);
var pagepost_res = HTTP.post(pagepost_endpoint, {params: pagepost_params});
console.log(pagepost_res);
I have played around a bunch with params vs. data for where pagepost_params goes in the HTTP.post that is giving the error (that is Meteor's HTTP btw).
-Putting everything in params gives the error: {"error":{"type":"Exception","message":"No Call To Action Type was parseable. Please refer to the call to action api documentation","code":1373054,"is_transient":false}}.
-Putting everything in data gives the error: {"error":{"message":"(#200) This API call requires a valid app_id.","type":"OAuthException","code":200}}.
-Putting access_token in params and everything else in data gives the error: {"error":{"message":"Invalid parameter","type":"FacebookApiException","code":100,"error_subcode":1349125}}.
One more clue for everyone, if I change the HTTP.post to HTTP.get, and just put access_token in params and include no other parameters (in params or in data), the call succeeds and I see past posts I have made on this page through the Graph Explorer (only the ones with published: true, though), so the access token and endpoint do work, just something is faulty about POST-ing instead of GET-ing and the specific parameters I'm using.
Have you tried posting to /photos instead of /feed? The error subcode is the same as mentioned here Posting to facebook wall using graph api
Hope this helps
Turned out to be an issue with Meteor's HTTP. It does not handle nested JSON very well, and we're going to submit a pull request for that. But for those seeing this, the important thing to take away is that the call_to_action may not be a valid JSON object, and even if it is, it may not be being stringified/parsed as expected. My fix was using request.post instead of HTTP.post. (then instead of params or data, you use form. look up node's request https://github.com/mikeal/request)

Facebook API - access token for web application

I am making a web app that pulls the latest posts from our Facebook page and processes them.
This is all working fine with a hard-coded access token generated from this page.
The problem is that this token expires, so i am looking for a solution to generate a new token every time the page loads or a non-expiring token - (i have read somewhere that non expiring tokens don't exist anymore).
So of course i did some research, here, here and here.
But non of these examples seem to be working.
Before any complaints of some code that i have tried so far, this is my working example - with an expiring access token:
var Facebook = function () {
this.token = 'MYTOKEN';
this.lastPost = parseInt((new Date().getTime()) / 1000);
this.posts = [];
};
Facebook.prototype.GetPosts = function () {
var self = this;
var deffered = $q.defer();
var url = 'https://graph.facebook.com/fql?q=SELECT created_time, message, attachment FROM stream WHERE created_time < ' + self.lastPost + ' AND source_id = 437526302958567 ORDER BY created_time desc LIMIT 5&?access_token=' + this.token + '';
$http.get(url)
.success(function (response) {
angular.forEach(response.data, function (post) {
self.posts.push(new Post(post.message, post.attachment.media, post.attachment.media[0].src, post.created_time, 'facebook'));
});
self.lastPost = response.data[response.data.length -1].created_time;
deffered.resolve(self.posts);
self.posts = [];
});
return deffered.promise;
};
return Facebook;
Any help / suggestion will be greatly appreciated.
First off, it is important to remember that Facebook has just launched the Version 2 of the Graph API. From April 2014 on, if you have issues with your app, you need to tell us when you created it on Facebook Developers (new apps use the Version 2 by default).
In order manage pages, your app needs to have manage_pages permission. Make sure that the user you want to manage fan pages for has authorized you. If your app uses the Version 2, make sure that Facebook (the Facebook staff) has authorized you to ask users that kind of permission, otherwise your app won't work.
Once you get your token, exchange it for a permanent token (or a token with long expiry date). Make sure you use the token of the fan page, not the token of the user.
If instead you want to read the stream of public fan pages, you need an access token with read_stream permissions. This permission needs to be approved by Facebook (see above) and this specific type of permission takes time to approve, if you're using the Version 2 of the Graph API. If you're using the old API (Version 1), you can still do that without pre-approval on Facebook's side.
The URL to ask for the permission to read the stream is as follows: https://www.facebook.com/dialog/oauth?client_id=$YOUR_APP_ID&redirect_uri=$YOUR_URL&scope=read_stream,manage_pages (i've added manage_pages in this case, you may not need it).
That url will prompt for authorization. Once the user has authorized the app, you'll be recirected to the URL you chose, with a code= variable.
At that point, call this other url:
https://graph.facebook.com/oauth/access_token?client_id={$app_id}&redirect_uri=$someurl&client_secret={$app_secret}&code={$code}
You'll get a response that has the access_token=variable in it. Grab that access token, exchange it for a long one, with the following URL:
https://graph.facebook.com/oauth/access_token?grant_type=fb_exchange_token&client_id={$app_id}&client_secret={$app_secret}&fb_exchange_token={$token_you_have_just_grabbed}
The response will give you a token that lasts for some time. Previously, Facebook had decided to have these "long duration tokens" expire after one month. I have found out, though, that they may have changed their mind: if you put a user token in the debugger, you'll see it never expires.
This is the authorization flow for users who visit with a browser. There's the app authorization flow too. If all you need is a stream from your own Fan page, you want to do the following (with Graph API V.1):
make an HTTP GET request using the following URL:
https://graph.facebook.com/oauth/access_token?type=client_cred&client_id={$app_id}&client_secret={$app_secret}
Use the resulting token to make another HTTP GET call, like so:
https://graph.facebook.com/{$your_page_id}/feed?{$authToken}&limit=10
//ten posts
Decode the json object
You're done.

Categories