I am struggling with a tutorial from Hasura which I believe might never have worked. I think it might be missing a piece and I'm interested in the most appropriate way to bridge the gap between when I've created and what will work.
From that documentation, as well as the Auth0 VueJS documentation I have been able to create a working VueJS application that authenticates via my Auth0 project and gives me back a user profile complete with user photo and email. So that's all working.
Following the Hausura tutorial from this I added apollo. I am now able to access non-protected GraphQL queries from my API but not protected ones. The reason is pretty clearly the code I added from the tutorial here:
getAuth: () => {
// get the authentication token from local storage if it exists
// return the headers to the context so httpLink can read them
const token = localStorage.getItem('apollo-token')
if (token) {
return 'Bearer ' + token
} else {
return ''
}
},
A console.log shows that token is null even when I'm logged in. It's pretty obvious why too. localStorage was never updated to contain a token. So of course it's null. There's no place in the tutorial that suggests this should be set anywhere but clearly it should.
So I did some digging and in that file src/vue-apollo.js there is an onLogin and onLogout function which require an ApolloClient object and in the case of the login, also a token. This will set the local storage appropriately.
Currently, my login is done within src/view/Home.vue like so:
login() {
this.$auth.loginWithRedirect()
// ... maybe do something here ?
},
Now if I were to add something like this after the login, it will not actually be called because of the redirect. But I think I need to add something like this:
this.$auth.getTokenSilently(token => {
onLogin(this.$apolloProvider.defaultClient, token)
}
Perhaps I need to add this into like the mounted() or beforeCreate() live cycle hook in my src/App.vue? I'm really not sure how to proceed here. Does this tutorial even really work?
Furthermore, isn't storing an auth token in localStorage a bad idea because of cross site request forgery?
You are right about it not being detailed in the post. I've not run the project but following the link to the source code of the demo. The /callback route creates the key via Callback component at /app/src/router.js:25, it add an authentication guard as well.
Then on /app/src/components/Callback.vue:25 on created handleAuthentication from auth service is called.
created() {
this.$auth.handleAuthentication();
}
You can check the code in /app/src/auth/authService.js it uses the auth0 library instance to get the token and call localLogin that saves the token in line 92.
About the security, I would follow the advice in Token Storage from Auth0.
Related
Working on a Firebase app that will help manage a users Google Calendar.
I am using the official Google Calendar Quickstart Guide code - https://developers.google.com/calendar/api/quickstart/js
Everything works great, I can sign in, authorize access and pull the calendar events.
Firebase allows you to log a user in by passing Firebase a Google ID token.
On this official Firebase guide, https://firebase.google.com/docs/auth/web/google-signin#expandable-2 it shows how to use the Sign In With Google library and then pass the resulting ID token to Firebase to sign in.
Everything works fine on the provided Firebase code until it get to this line.
const idToken = response.credential;
The token returned to the Google Sign In callback doesn't include a credential.
The object has these properties:
access_token, expires_in, scope, token_type
So when I try to access the .credential on the response it is undefined, so the resulting call to login to Firebase with that credential fails.
The new Sign In With Google flow separates the authentication and authorization. https://developers.google.com/identity/gsi/web/guides/overview#separated_authentication_and_authorization_moments and states
"To enforce this separation, the authentication API can only return ID
tokens which are used to sign in to your website, whereas the
authorization API can only return code or access tokens which are used
only for data access but not sign-in."
Something seems strange because it appears the token being returned is the Google Calendar data access token, when I thought it would be the Google Sign in token.
I've googled every combination, and read any related SO answer I can think of trying to fix this, seems like I am missing something simple.
Figure out it was the wrong token, because when I removed everything out and just tried to implement a basic Sign In With Google, that token works.
Used the Google provided button/popup that their library provides from their guide here:
<div id="g_id_onload"
data-client_id="CLIENT_ID_GOES_HERE"
data-callback="handleCredentialResponse">
</div>
In the handleCredentialResponse callback, the returned token did have a .credential
Passing that to Firebase worked to login.
const idToken = response.credential;
const provider = new firebase.auth.GoogleAuthProvider();
const credential = provider.credential(idToken);
auth.signInWithCredential(credential).catch((error) => {
// Handle Errors here.
});
So obviously I wasn't understanding what was happening in the Google Quick start example.
Now I assume I can use the Google Sign In Token to request Calendar OAuth permissions.
I am having a react application with firebase as authentication. My authentication code is below
await firebase.auth().onAuthStateChanged((user) => {
if (user) {
props.setUser(user); //setting the user if login/register happens
history.push(`/admin/dashboard`);
console.log("user",user)
} else {
props.setUser(null); //blocks the user to get into the app if he/she is not logged in
history.push("/");
}
});
So, when user logs in..he will be navigated to /admin/dashboard. suppose when am in /admin/home and when i refresh the page, it goes again to admin/dashboard which shouldn't happen. so I tried
history.push(${props.location.pathname}); it works correctly after the refresh, it stays on the same page when the application is logged in. but when I restart the server again when I try to log in, it says no redirect url is specified. Got stuck on this for a long time.. Any help is welcome.Thanks
What your code does is check if the user is logged in and only let the user access the data if so.
You should do that in the fireabse rules (= serverside) as this is way more secure.
You didn't provide the kind of FirebaseDB you are using. So assuming you use the Realtime Database here are some according rules:
{
“rules”: {
“.read”: “auth != null”,
“.write”: “auth != null”
}
}
You should maybe check the rules before deploying your app, because now every authenticated user can change/add/delete data, but you get the point. This does exactly what you want so you won't even need to perform a check in your ReactJS App. Firebase will automatically deny unauthenticated users the access to the database.
Btw: You should try to implement security relevant things in the Firebase Rules. Ideally you want your rules to be written in a way that you don't need to perform any validation inside your ReactJS app. Firebase rules can get quite complex. I experienced that myself when writing a chat app with chatrooms and everything. But it is definitly worth the effort if your app is more secure after.
Using Angular 2+ with #azure/msal-angular library.
I have an app with the domain
http://localhost:4200/userId/someOtherId
So it can be any of
http://localhost:4200/2425/2532152
http://localhost:4200/35235/152115
I have a button, Login with Microsoft. On clicking that button, I call the sign in method
import { MsalService } from '#azure/msal-angular';
/// more code
signIn() {
await this.msalService.loginPopup(environment.microsoft.scopes);
//more code
}
(See sign in method here: https://learn.microsoft.com/en-us/graph/tutorials/angular?tutorial-step=3 , I do the same thing)
Now, in the application registration portal, I have
http://localhost:4200 as my redirect URI.
As a result, when I attempt to authenticate, I get the following error:
The reply url specified in the request does not match the reply urls
configured for the application
My question is, how do I solve this problem? Someone said I should be passing in state &state=userId:someotherId, but how do I do that with microsoft's authentication library for angular?
The UserAgentApplication accepts state as a property of the options object in the constructor.
However, when they created MSAL-Service which derives from UserAgentApplication it looks like they didn't expose the state parameter. I would recommend opening an issue on the GitHub repo.
I am using vue js as a front end and after authenticating user from my appi built with laravel I am receiving a token which is supposed to send with every consequent request for authenticating the api.
But how should I store the token in the browser Securely?
Your question is very broad. So I'm gonna give a general answer.
Use localStorageto store the token.
localStorage.setItem('name','tokenValue'); // to store the token
localStorage.getItem('name'); // to getthe token value
A simple web search will give you all you need to know about localStorage. Hope this helps.
Answer that I found here is very inefficient because localStorage is not assync so after login you would need to refresh entire page which is very bad UX to refresh single page app which I believe is what you develop since you use vue.
What I can suggest istead is localForage which is nothing more than assync localStorage. It is promise based and syntax looks very similiar to localStorage.
Example:
localforage.getItem('something', myCallback);
More info in its docs.
And to send token in every request to backend you should use vue.http.interceptors.
Example:
Vue.http.interceptors.push((request, next) => {
request.headers['Authorization'] = auth.getAuthHeader()
next((response) => {
if(response.status == 401 ) {
auth.logout();
router.go('/login?unauthorized=1');
}
});
});
I've written several Google Cloud Endpoints in Python and have followed the directions to require that calls to them come from users authenticated using Firebase. I need to call my Endpoints from a web app using JavaScript, but I can't seem to get the authentication working.
I'd like to use the Google APIs client (gapi) which comes with the added benefit of dynamically generating the client library from a provided discovery document. When I try using the gapi client, I can make the call to my API just fine, but I get an HTTP 401 as a response, along with the HTTP unauthorized message that my python source returns.
Google's documentation on the subject is rather sparse. I gather from one tutorial on the subject that a standard Ajax call can be used, but I don't see any documentation on how to call a Firebase authenticated endpoint from Gapi. My current concern is that the gapi client may not be set up (yet) to allow for the use of a discovery doc and also allow for the Authorization header to be set as Firebase Auth requires.
Is what I'm attempting even possible?
Any suggestions would be appreciated. Perhaps calling a Firebase Authenticated endpoint isn't possible using the gapi client.
Here's a rough outline of my gapi js code:
function(token) {
gapi.client.init({
apiKey: 'MY_API_KEY',
discoveryDocs: [MY_DISCOVERY_DOC_URL'],
clientId: 'MY_WEB_CLIENT_ID',
scope: 'profile'
}).then(function(){
return gapi.client.my.server.api.call();
}).then(function(response){
console.log(response.result.data)
}, function(reason){
console.log('Error: ' + reason.result.error.message)
});
}
I have been struggling with this for a while now and finally made it work. I found two options:
Option 1) If you want to use the gapi.client library:
There is a method called gapi.client.setToken(tokenObject) - documentation
However, it seems to be new (July '17) and little documentation or examples are available. I made it work doing the following (this is in angularJS with angular-fire but I hope you get what I am doing, basically ignore the "$scope")
// any time auth state changes, add the user data to scope
$scope.auth.$onAuthStateChanged(function (firebaseUser) {
$scope.firebaseUser = firebaseUser;
$scope.idToken = null;
// get the token from the firebase User Object
// Note that getToken() is deprecated and for me it did not work as desired
// use getIdToken() instead
firebaseUser.getIdToken().then(function (idToken) {
$scope.idToken = idToken;
});
});
// Now you can use setToken
// If from the docs you were thinking firebase's getIdToken() gives me TokenObject and gapi's setToken()
// expects a TokenObject so I'll just pass it - you'd be wrong! (at least for me - if it works for you please give me a heads up)
// You'll need to build your own token:
var homemadeToken = {
access_token: $scope.idToken.toString() // This feels so wrong
};
gapi.client.setToken(homemadeToken);
gapi.client.yourapi.getSomething().execute(function (resp) {
// Do stuff with the response
}
);
Option 2) Use jQuery's Ajax request - documentation
$.ajax(backendHostUrl + '/_ah/api/yourapi/v1/someendpoint', {
headers: {
'Authorization': 'Bearer ' + $scope.idToken // Here it worked without making a string first but I did not check why
},
method: 'GET',
success: function (resp) {
// Do stuff with the response
}
});
If after all of that your backend is still not accepting the tokens and you have migrated from endpoints v1 to v2, it might help migrating again as described here. Esp. make sure the lib folder is created again.
Even after SDK updates, I noticed that if and once you migrated from v1 to v2 the "lib" folder is never updated regardless of whether or not it hase been updated.
Still not working?
This github page fixes the issue on the BACKEND side for an earlier version - the backend did not accept firebase tokens and needed to be hacked. If you want to apply the changes as described there and you are using the latest "lib" folder's (writing in July '17) users_id_token.py as per migration guide, note that the file has changed and you need to go against the explicit commentary in that file's _verify_signed_jwt_with_certs method:
# Formerly we would parse the token body here.
# However, it's not safe to do that without first checking the signature.
and parse the token before checking the signature. From that file's comments it can be inferred however, that Google plans to put the entire logic elsewhere - hopefully firebase friendly and safely.