Using Firebase Auth to access the Google Calendar API - angularjs

I am building a web application using AngularJS, Firebase (SDK v3) and Google Calendar API. I'm authenticating users using Google OAuth. My purpose is to be able to create calendar events from database nodes in Firebase. So far I've managed to request access to the calendar scope with:
_authProvider = new firebase.auth.GoogleAuthProvider();
// Get permission to manage Calendar
_authProvider.addScope("https://www.googleapis.com/auth/calendar");
_fbAuthObject.signInWithRedirect(_authProvider);
I'm authenticating with the redirect flow so the authentication redirect is available as:
_fbAuthObject.getRedirectResult()
.then(function _readToken(result) {
if (result.credential) {
_googleToken = result.credential.accessToken;
var authHeader = 'Bearer '+ _googleToken;
// Just a test call to the api, returns 200 OK
$http({
method: 'GET',
headers: {
'Authorization': authHeader
},
url: 'https://www.googleapis.com/calendar/v3/users/me/calendarList/primary'
})
.then(function success(response) {
console.log('Cal response', response);
},
function error(response) {
console.log('Error', response);
});
However, it seems like outside the initial login it's not possible to get the Google access token through the Firebase SDK. It seems only possible to access the Firebase JWT token, no use with the Calendar API. I could store the access token, but this wouldn't resolve the problems when refreshing the token, etc. Is there any way to get the current Google Access token with Firebase SDK and if not, what other solutions is there to the problem without having to authenticate the user twice?
UPDATE 1:
Seems like someone else has struggled with similar problems with Facebook authentication. On that question there was a link to the Firebase documentation stating that Firebase Authentication no longer persists the access token. So how can I handle token refreshes? Is there really no answer to this?
UPDATE 2:
So, I contacted Firebase Support with a feature request about this problem and they gave me the following answer:
Thanks for taking your time to write us.
I've got your point here, this is indeed a good suggestion. We're definitely aware that many users, such as yourself, would like OAuth feature that will access token upon refresh. We're exploring potential solutions, but I cannot guarantee if this will be available anytime soon. We'll keep your feedback in consideration moving forward though. Continuous improvement is very important for our community, so thanks for bringing this up!
Keep an eye out on our release notes for any further updates.
So It seems like the access tokens are not available through the Firebase SDK at the moment. I'm still trying to find a workaround, so if anyone has ideas about a valid solution I'd be glad to hear them. And of course, I'll be posting it here if I ever find a working solution myself.

I finally got around this problem by handling the authentication outside Firebase with the Google APIs JavaScript client. This solution requires including the Google auth client as documented here. Manually handling the Firebase sign-in flow is documented here.
gapi.auth2.getAuthInstance().signIn()
.then(function _firebaseSignIn(googleUser) {
var unsubscribe = firebase.auth().onAuthStateChanged(function(firebaseUser) {
unsubscribe();
// Check if we are already signed-in Firebase with the correct user.
if (!_isUserEqual(googleUser, firebaseUser)) {
// Build Firebase credential with the Google ID token.
var credential = firebase.auth.GoogleAuthProvider.credential(
googleUser.getAuthResponse().id_token);
// Sign in with credential from the Google user.
return firebase.auth().signInWithCredential(credential)
.then(function(result) {
// other stuff...
});
The _isUserEqual function:
function _isUserEqual(googleUser, firebaseUser) {
if (firebaseUser) {
var providerData = firebaseUser.providerData;
for (var i = 0; i < providerData.length; i++) {
if (providerData[i].providerId === firebase.auth.GoogleAuthProvider.PROVIDER_ID &&
providerData[i].uid === googleUser.getBasicProfile().getId()) {
// We don't need to reauth the Firebase connection.
return true;
}
}
}
return false;
}
Now I can reference the access token like this:
var user = gapi.auth2.getAuthInstance().currentUser.get();
return user.getAuthResponse().access_token;
This still isn't the ideal solution for me, but it works for now, and I'm able to authenticate to both Firebase and the Calendar API.

Related

Azure Web App with Acitve Directory Express with Graph API to get user photo

My Azure Web App has Active Directory enabled using the Express option. I can get the user claims/user's name from auth.me. How do I then get the user's photo/avatar? The token I get is not working in a Graph API call. I get this error from Graph API. Here is my code.
Please help! Spent hours searching and reading docs but nothing seems to address the Express AD scenario.
Thanks
Donnie
{
"error": {
"code": "InvalidAuthenticationToken",
"message": "CompactToken parsing failed with error code: 80049217",
"innerError": {
"request-id": "e25f1fe5-4ede-4966-93c2-6d92d34da6ae",
"date": "2019-03-13T14:13:26"
}
}
}
axios.get('/.auth/me').then(resp => {
if(resp.data){
loggedInUser = {
accessToken:resp.data[0].access_token,
userId: resp.data[0].user_id,
username: resp.data[0].user_claims[9].val,
lastname: resp.data[0].user_claims[8].val,
fullname: resp.data[0].user_claims[11].val,
avatar:'https://cdn.vuetifyjs.com/images/lists/1.jpg'
}
let config = {
'headers':{
'Authorization': 'Bearer ' + loggedInUser.accessToken
}
}
axios.get('https://graph.microsoft.com/v1.0/me/photos/48x48/$value',config).then(resp => {
let photo = resp.data;
const url = window.URL || window.webkitURL;
const blobUrl = url.createObjectURL(photo);
document.getElementById('avatar').setAttribute("src", blobUrl);
loggedInUser.avatar = blobUrl;
console.log(blobUrl)
});
}
})
I was able to pull the image using MSDAL to handle the token. The new App Registration blade (as of 4/10/2019 is in preview) has a quick start which will ensure your app registration is correctly configure and allow you to download sample code.
In this blade, make sure you've added graph API permissions as shown below. When you click on Quick Start, you'll get a sample similar to this gist. It makes use of MSAL js library which handles the token negotiation.
var myMSALObj = new Msal.UserAgentApplication(applicationConfig.clientID, applicationConfig.authority,
acquireTokenRedirectCallBack, {
storeAuthStateInCookie: true,
cacheLocation: "localStorage"
});
function signIn() {
myMSALObj.loginPopup(applicationConfig.graphScopes).then(function (idToken) {
//Login Success
showWelcomeMessage();
acquireTokenPopupAndCallMSGraph();
}, function (error) {
console.log(error);
});
}
After that, the magic happens in acquireTokenPopupAndCallMSGraph() which will acquire the token so you can use it to call the graph API. Now my gist makes use of XMLHttpRequest which I'm sure you'll be able to replace with axios.
To get the photo in the v1.0, it supports only a user's work or school mailboxes and not personal mailboxes.
For the details, you could read here.
Your AD app registration may not have the necessary delegate permissions. To add those permissions to your app, see these steps. I think you may need to use the oauth (login.microsoftonline.com/{{tenant}}/oauth2/v2.0/token) endpoint rather than .auth/me. With the oauth endpoint, you can even elect to pass in the scopes your token needs for calling the graph API. You can use http://jwt.ms to decode the token and see if has the necessary delegate permissions.
Also, I came across this blog series that lists various tutorials for working the Microsoft Graph. You can also check out https://github.com/microsoftgraph/nodejs-apponlytoken-rest-sample.
Furthermore, https://github.com/microsoftgraph/nodejs-connect-rest-sample makes use of passport and passport-azure-ad npm packages. That actually may be more advantagous to getting and managing tokens from Azure AD.
Hope this helps.
Ryan, I added delegate permissions to my web app's permissions settings for reading user profiles, but I still get the error message when tying to get profile pic from graph. Not sure what permissions it needs, but I basically gave it full access to use's profile. Graph just doesn't seem to like the token provided by AD Express config (login.microsoftonline.com)
{
"error": {
"code": "InvalidAuthenticationToken",
"message": "CompactToken parsing failed with error code: 80049217",
"innerError": {
"request-id": "e25f1fe5-4ede-4966-93c2-6d92d34da6ae",
"date": "2019-03-13T14:13:26"
}
}
}
Ryan, jwt fails when I paste the full token from auth/me .

Refreshing token through msal.js

I'm using Azure AD B2C for my React.js app and I've noticed that after a while, all user requests are getting rejected by my API as unauthorized.
I'm pretty sure the issue is that the jwt token expires. I'm using msal.js to get my token from Azure AD B2C.
I found a short paragraph on Microsoft Docs but couldn't find any examples. Also my research indicates I need to open up a new windows and manually make an HTTP request. Is this correct?
Could someone tell me where I can find some examples of this?
Always call acquireTokenSilent before you call your API. Let MSAL do the caching, refreshing etc., that is what it is for. Fallback to AcquireTokenPopup or similar if silent fails.
https://github.com/Azure-Samples/active-directory-b2c-javascript-msal-singlepageapp
function callApi() {
clientApplication.acquireTokenSilent(applicationConfig.b2cScopes).then(function (accessToken) {
callApiWithAccessToken(accessToken);
}, function (error) {
clientApplication.acquireTokenPopup(applicationConfig.b2cScopes).then(function (accessToken) {
callApiWithAccessToken(accessToken);
}, function (error) {
logMessage("Error acquiring the access token to call the Web api:\n" + error);
});
})
}
Side note: For acquireTokenSilent, MSAL is actually creating a hidden iframe to acquire the token, which might be what you are referring to in your question.

Django rest framework token authentication AngularJS

I am fairly new to Django and Token authentication as well as AngularJS. So far I have made Django backend and AngularJS frontend. I installed the Django Rest Framework and got the standard token authentication working as far as doing:
$scope.login = function () {
$http.post('link to my api', {
'username': $scope.data.username,
'password': $scope.data.password,
}).success(function () {
$state.go('tab');
})
.error(function () {
});
};
So far I succeed by getting a token with the right username and password. I see so much options about authentication that I don't know what fits for me anymore and where to start as I don't get a lot of information on the standard Token authentication. I have an Ionic app with a login screen, the Django backend has the users stored. I want to be able to login with a user account that resides within the Django database. Whenever that is done the user got permission to use the app. Later on it should be possible for example to get the credentials like personal details of the logged in user. Let's say I use the TokenAuthentication like above, I get the token. But how can I get the user credentials or whatever else from Django later on by that token? I am totally lost. If someone could please bring me into the right approach and material.
I found out that when you implement the following DRF permissions:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
)
}
and you make viewsets like on http://www.django-rest-framework.org/#example that it automatically requires a token to view those viewsets. So then you could work with that token to send requests to the api to get specific data.

Angularfire/Firebase Facebook Authentication - A Better Profile Picture [duplicate]

I am using firebase facebook simple login.
Is there any way I can use it conjunction with facebook js graph api?
Let say, calling FB.api('xxx', function(){}) ?
Facebook via Firebase Simple Login returns the Facebook access token as part of the payload. You could then use this directly with the Facebook Graph API and their JavaScript SDK:
var ref = new Firebase(URL);
var auth = new FirebaseSimpleLogin(ref, function(error, user) {
if (user) {
var facebookToken = user.accessToken; // <<-- here it is
}
});
// Note: Attach this to a click event to permit the pop-up to be shown
auth.login('facebook');
As you noted, Singly is another great approach that abstracts some of the effort of talking to Facebook, but shouldn't be necessary if your use case is fairly straightforward.
UPDATE
Firebase Simple Login now supports authenticating a user with an existing Facebook access token, meaning that you can easily use the Facebook JS SDK in combination with Firebase Simple Login without asking your users to authenticate twice.
For example, once you have a valid Facebook access token using the Facebook JS SDK:
var ref = new Firebase(...);
var auth = new FirebaseSimpleLogin(ref, function(error, user) { ... });
auth.login('facebook', { access_token: '<ACCESS_TOKEN>' });
See the access_token option at https://www.firebase.com/docs/security/simple-login-facebook.html for more information.

google app script consumer with google appEngine Provider (Oauth)

I could get an Oauth conexion between GAS and GAE.
I built a Google Gadget which needs some data from my datastore application, it has 3 end points to finally get an access token.
http//[myapp].appspot.com/_ah/OAuthGetRequestToken
http//[myapp].appspot.com/_ah/OAuthAuthorizeToken
http//[myapp].appspot.com/_ah/OAuthGetAccessToken
In GAS side i have the tipical function to get an access token.
function oauthTokenFetch(){
var oAuthConfig = UrlFetchApp.addOAuthService("myAppName");
oAuthConfig.setAccessTokenUrl("https://<myApp>appspot.com/_ah/OAuthGetAccessToken");
oAuthConfig.setRequestTokenUrl("https://<myApp>.appspot.com/_ah/OAuthGetRequestToken");
oAuthConfig.setAuthorizationUrl("https://<myApp>.appspot.com/_ah/OAuthAuthorizeToken");
oAuthConfig.setConsumerKey("<myApp>.appspot.com");
oAuthConfig.setConsumerSecret("myConsumerSecret");
var requestData = {
"method": "GET",
"oAuthServiceName": "myAppName",
"oAuthUseToken": "always"
};
try {
var response2=UrlFetchApp.fetch("http://<myApp>.appspot.com/test/oauth",requestData);
Logger.log(response2.getResponseCode());
}catch(exception){
Logger.log(exception);
}
If i revoked the access token on google accounts, it isn't able for getting another one, the popup which grants the authorization don't appears ... i have to copy the google gadget and do the authorization again.
Somebody have a Solution?
There currently isn't a method to allow you to revoke or remove an OAuth token in a script. It looks like you already filed a bug/feature request on the topic, and we'll follow up there.

Resources