Calling API from JavaScript Frontend: 401 unauthorized using angularfire - angularjs

I am following this tutorial on adding firebase authentication to my webapp using endpoints v2 (migrated from v1 yesterday).
I previously had google accounts authentication only but want to switch to firebase for adding e.g. facebook.
When I call my API from my JavaScript Frontend (built with angularJS & angularfire) I get a 401 unauthorized.
I feel, I am missing a logical step:
I can log-in on the client-side (pop-up opens and my facebook-name is displayed).
Missing step?
endpoints.get_current_user() does not get a user.
Where am I going wrong?
This is what I want to initialize the page where I want to get a profile from the backend:
`/**
* Initialize profile page.
* Update the profile if the user's profile has been stored.
*/
$scope.init = function () {
var retrieveProfileCallback = function () {
$scope.profile = {};
$scope.loading = true;
gapi.client.myapi.getProfile().execute(function (resp) {
$scope.$apply(function () {
$scope.loading = false;
if (resp.error) {
// Failed to get a user profile.
} else {
// Succeeded to get the user profile.
$scope.profile.displayName = resp.result.displayName;
$scope.profile.someOtherProperty = resp.result.someOtherProperty;
$scope.initialProfile = resp.result;
}
});
}
);
};
if (!firebaseUser) {
//TODO
} else {
retrieveProfileCallback();
}
};`
This is the start of the method that is ultimatively called from getProfile()-endpoint:
def _getProfileFromUser(self):
"""Return user Profile from datastore, creating new one if non-existent."""
## Make sure user is authed
user = endpoints.get_current_user()
if not user:
raise endpoints.UnauthorizedException('Authorization required')
Here is my API decorator (openapi.json has been deployed):
# - - - - firebase - - - - - - - - - - - - - - - - - -
firebase_issuer = endpoints.Issuer(
issuer='https://securetoken.google.com/appname-123456',
jwks_uri='https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken#system.gserviceaccount.com')
# - - - - Endpoints API - - - - - - - - - - - - - - - - - - -
#endpoints.api(name='myapi',
version='v1',
scopes=[EMAIL_SCOPE],
issuers={'firebase': firebase_issuer})
class MyApi(remote.Service):
I feel like I am massively misinterpreting the tutorials. It seems too easy and does not work.
E.g. for the google accounts authorization I initialized the oauth2 api like so in the index.html:
`<script>
function init() {
gapi.client.load('myapi', 'v1', null, '//' + window.location.host + '/_ah/api');
gapi.client.load('oauth2', 'v2', function () {
angular.bootstrap(document, ['conferenceApp']);
});
};
</script>`
I took that out because I figured I am switiching to firebase.
Like so:
`<script>
/**
* Initializes the Google API JavaScript client. Bootstrap the angular module after loading the Google libraries
* so that Google JavaScript library ready in the angular modules.
*/
function init() {
gapi.client.load('myapi', 'v1', null, '//' + window.location.host + '/_ah/api', function () {
angular.bootstrap(document, ['myApp']);
});
};
</script>`

The openapi configuration looks fine. The problem is with the client code, which handles sign-in flow to get a Firebase auth token. You need to use Firebase web SDK instead of Google JS SDK. You can take a look at the instructions at https://firebase.google.com/docs/auth/web/google-signin.

Related

Google API on Published Electron app: gapi.auth2.ExternallyVisibleError: Invalid cookiePolicy

I have a React + Electron app using Google API to authenticate and get a list of calendar events.
The API script is being loaded on the head of my index.html and initialised on my App.js like so:
// Initializes the API client library and sets up sign-in state listeners.
initClient() {
let gapi = window["gapi"];
let that = this;
gapi.load("client", start);
function start() {
gapi.client
.init({
apiKey: GOOGLE_API_KEY,
clientId: CLIENT_ID,
discoveryDocs: [
"https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest"
],
scope: "https://www.googleapis.com/auth/calendar.readonly"
})
.then(() => {
gapi.auth2
.getAuthInstance()
.isSignedIn.listen(that.updateSigninStatus);
that.updateSigninStatus(
gapi.auth2.getAuthInstance().isSignedIn.get()
);
that.setState({
apiLoaded: true
});
});
}
}
It works completely fine on a local environment, where I have a server running, but once I build my Electron app and run the app "natively", I get the following error: gapi.auth2.ExternallyVisibleError: Invalid cookiePolicy
I don't have an advanced understanding of APIs and Servers to figure this out but through research, I found something about the API not working from a "file://" protocol, which is the case on an Electron app.
Thoughts? Ideas?

implement wechat authentication in cordova hybrid app ios

I am developing a cordova hybrid app targeting ios & need to add authentication via wechat in login page.
$scope.wechatLogin=function(){
scope = "snsapi_userinfo",state = "_" + (+new Date());
Wechat.auth(scope, state, function (response) {
alert(JSON.stringify(response));
$.get('https://api.weixin.qq.com/sns/oauth2/access_token?myappid=' + appId + '&secret=' + myappSecret + '&code=' + response.code + '&grant_type=authorization_code', function (accessTokenResponse) {
alert(JSON.stringify(accessTokenResponse));
// you should save access token response somewhere, e.g. cookies, local storage, etc.
var accessToken = accessTokenResponse.access_token;
var openId = accessTokenResponse.openid;
// get user information
$.get('https://api.weixin.qq.com/sns/userinfo?access_token=' + accessToken + '&openid=' + openId + '&lang=zh_CN', function (userInfoResponse) {
console.log(userInfoResponse);
});
});
});
}
when testing this in device, it opens wechat & gives a message as "oops! Something went wrong". Can You help me to resolve this.
Thank you very much!

integrating linkedin login with angularjs application

I am new to angular js .I would like to know the procedure to get api key and integrating linked in sign in ,sign up with angularjs application.
You can use LinkedIn SDK to handle authorisation and sing up or sing off users.
Documentation: https://developer.linkedin.com/docs/getting-started-js-sdk
You have to initial SDK. You will need to create an app in your LinkedIn Developer panel. Then you get an API Key there.
Then in your app you have to create a service that will call LinkedIn API.
Something like this:
export class LinkedIn {
constructor($q, $window) {
'ngInject';
this.$q = $q;
this.$window = $window;
};
get() {
let doc = this.$window.document;
let script = doc.createElement('script');
let deferred = this.$q.defer();
script.src = 'http://platform.linkedin.com/in.js';
script.innerHTML = [
'api_key: ' + YOUR_API_KEY,
'authorize: ' + 'true',
'lang: ' + 'en-US',
'onLoad: onLinkedInApiLoad',
'scope: ' + YOUR_APP_SCOPE
].join('\n');
this.$window.onLinkedInApiLoad = () => {
deferred.resolve(this.$window.IN);
};
doc.body.appendChild(script);
return deferred.promise;
};
}
Next you need to decide where and when you want to initial this call. You can do that in .run block or made some middleware to handle it. After that you will receive LinkedIn API object.
When you have got your LinkedIn API object you can request authorisation, check if user has been already logged in and etc. Options are describe in documentation. You can authorise user calling IN.User.authorize(handler || angular.noop) or logout IN.User.logout(handler || angular.noop)
There is also options to do a callback on Event where user log in or log out for example:
IN.Event.on(IN, eventName, callback, callbackScope, extraData);
IN.Event.onOnce(IN, eventName, callback, callbackScope, extraData);
You can use angular module for linkedIn authentication. angular-social-login

Login with Google using Ionic framework gives 404 on callback

I am trying to implement login with Google in an Ionic browser based app. These are the commands I ran to install and create my app:
ionic start tracker sidemenu
cd tracker
cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-inappbrowser.git
ionic platform add browser
When I click my login button, the Google auth comes up ok. However when the callback is called I get a 404:
Failed to load resource: the server responded with a status of 404 (Not Found)
http://localhost:8100/callback?code=xxxxxx
This OAuth 2 Ionic example gave this code:
var clientId = 'xxxxxx-3rpu226qm-xxxxx.apps.googleusercontent.com';
var scope = 'profile email';
var ref = window.open('https://accounts.google.com/o/oauth2/auth?client_id=' + clientId +
'&redirect_uri=http://localhost:8100/callback&scope=https://www.googleapis.com/auth/urlshortener&approval_prompt=force&response_type=code&access_type=offline',
'_blank', 'location=no');
ref.addEventListener('loadstart', function (event) {
console.log('listener event.url: ' + event.url);
if ((event.url).startsWith("http://localhost:8100/callback")) {
var requestToken = (event.url).split("code=")[1];
console.log('request token from google: ' + requestToken);
ref.close();
}
});
Why isn't the eventListener catching the callback?
Its a known issue of the cordova-plugin-inappbrowser that loadstart and loaderror events are not being fired in the browser.
Source: https://github.com/apache/cordova-plugin-inappbrowser#browser-quirks-1
You might want to look at an other javascript library like hello.js for the browser case (https://adodson.com/hello.js/#hellojs). Or implement it yourself. Here you can find an example for how to do it for google oath2 yourself: https://developers.google.com/identity/protocols/OAuth2UserAgent

bacbkone router redirect if not authenticated

I am trying to implement a simple app that needs a login and user authentication. As I am new to backbone and marionette, I have been trying to follow the example for this tutorial: https://github.com/davidsulc/marionette-gentle-introduction
Generally I have set up a new app:
var App = new Marionette.Application({});
App.addRegions({
headerRegion : "#nav-region",
mainRegion : "#main-region"
});
App.navigate = function(route, options){
options || (options = {});
Backbone.history.navigate(route, options);
};
App.getCurrentRoute = function(){
return Backbone.history.fragment
};
App.on("start", function(){
if(Backbone.history){
Backbone.history.start();
}
});
And routers are defined in modules, e.g.:
App.module("ContentManagementApp", function(ContentManagementApp, App, Backbone, Marionette, $, _){
ContentManagementApp.Router = Marionette.AppRouter.extend({
appRoutes : {
"contentmanagement/:dsid(/:dspageclassid)": "showContentMananagement",
}
});
var API = {
showContentMananagement : function(dsid, dspageclassid){
// If not set, set to frontpage
ContentManagementApp.Show.Controller.showDSPage(dsid, dspageclassid);
App.execute("set:active:header", "contentmanagement");
},
};
App.on("contentmanagement:show", function(dsid, dspageclassid){
App.navigate("contentmanagement/" + dsid + "/" + dspageclassid);
API.showContentMananagement(dsid, dspageclassid);
});
App.addInitializer(function(){
new ContentManagementApp.Router({
controller : API
});
});
});
I would like to test if the user is logged and redirect to the login page when the app starts, but it seems like App.addInitializer is called before. Does it mean I have to do the check in each module, or can I get by it somehow?
How do you determine if the user is logged or not?
If it's a call to an API that could fail (due to the user being unauthenticated), it will probably return an HTTP error code 403. I usually do this using a global jQuery ajax.error() handler, I check if it's a 403 (Forbidden) for any of my normal API calls (model fetching and so on) and if it is, I redirect to a login url.
Otherwise, if you want to check for a cookie or similar, you should do it before calling Backbone.history.start(). Only start the app if the user is logged. :)
I just set this up in my app - in your backend when the user is logged in create a cookie/destroy it when they sign-out. Then I use the jquery-cookie-rails gem to access the cookie as $.cookie('cookie_name') and if it isn't there I route them to the signin path.
I would note - I also check to see if the user is signed on the backend when hitting different controller actions and route them appropriately. I just like the extra protection :).

Resources