mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-11-11 16:27:14 +03:00
parent
80a5f00ae4
commit
e460ec0e62
4
community/examples/firebase-jwt/.gitignore
vendored
Normal file
4
community/examples/firebase-jwt/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
firebase.json
|
||||
.firebaserc
|
||||
node_modules
|
||||
config.js
|
64
community/examples/firebase-jwt/README.md
Normal file
64
community/examples/firebase-jwt/README.md
Normal file
@ -0,0 +1,64 @@
|
||||
# Firebase Auth + Hasura JWT
|
||||
|
||||
Barebones example to show how to have Firebase Auth integrated with Hasura JWT mode.
|
||||
|
||||
## Firebase Auth
|
||||
|
||||
Firebase has few ways of implementing custom JWT claims in Firebase Auth:
|
||||
|
||||
1. Have firebase generate the JWTs, then customize them from your backend using
|
||||
Firebase Admin SDK [[docs]](https://firebase.google.com/docs/auth/admin/custom-claims#defining_roles_via_an_http_request)
|
||||
2. Use Firebase cloud functions, and listen to user creation events to add
|
||||
custom claims to generated JWT [[docs]](https://firebase.google.com/docs/auth/admin/custom-claims#defining_roles_via_firebase_functions_on_user_creation)
|
||||
3. Have your own backend server, which generates custom tokens [[docs]](https://firebase.google.com/docs/auth/admin/create-custom-tokens)
|
||||
4. Have your own backend scripts (not initiated by the client) to update user custom claims [[docs]](https://firebase.google.com/docs/auth/admin/custom-claims#defining_roles_via_backend_script)
|
||||
|
||||
## Add custom claims in Firebase
|
||||
|
||||
In this example, we are choosing the option 2 from above. But this can be done via any of the above methods. [Firebase docs](https://firebase.google.com/docs/auth/admin/custom-claims) have extensive documentation on how to achive this via different methods.
|
||||
|
||||
This example is adapted from [this guide](https://firebase.google.com/docs/auth/admin/custom-claims#defining_roles_via_firebase_functions_on_user_creation).
|
||||
|
||||
### Pre-requisites
|
||||
|
||||
This example assumes that you already have Firebase Auth setup for your app.
|
||||
|
||||
### Add the cloud function
|
||||
|
||||
Deploy the cloud function inside `functions/` folder:
|
||||
|
||||
```shell
|
||||
firebase deploy --only functions
|
||||
```
|
||||
|
||||
Customize the code to add your logic of assigning roles in the custom claims.
|
||||
|
||||
This cloud function is using the `onCreate` trigger. So whenever a user is created, this function is run.
|
||||
|
||||
### Client-side code
|
||||
|
||||
The client-side code is in `app/` folder.
|
||||
|
||||
## Configure Hasura to start in JWT mode
|
||||
|
||||
Deploy Hasura GraphQL Engine on Heroku:
|
||||
|
||||
[![Deploy HGE on heroku](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/hasura/graphql-engine-heroku)
|
||||
|
||||
After deploying, add the following environment variables to configure JWT mode:
|
||||
|
||||
```
|
||||
HASURA_GRAPHQL_ACCESS_KEY : yoursecretaccesskey
|
||||
```
|
||||
|
||||
```
|
||||
HASURA_GRAPHQL_JWT_SECRET: {"type":"RS512", "jwk_url": "https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com"}
|
||||
```
|
||||
|
||||
## Sending JWT to Hasura
|
||||
|
||||
Now, whenever you make a request to Hasura GraphQL engine (as an authenticated user), send the `id_token` in `Authorization` header:
|
||||
|
||||
```
|
||||
Authorization: Bearer <firebase-id-token>
|
||||
```
|
17
community/examples/firebase-jwt/app/index.html
Normal file
17
community/examples/firebase-jwt/app/index.html
Normal file
@ -0,0 +1,17 @@
|
||||
<html>
|
||||
<head>
|
||||
<title> Firebase Auth + Hasura JWT example </title>
|
||||
</head>
|
||||
<body>
|
||||
<h1> Firebase Auth + Hasura JWT example </h1>
|
||||
<form id="login-form">
|
||||
Email: <input id="email" type="text"/>
|
||||
Password: <input id="password" type="password" />
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
<button id="get-token"> Get ID token </button>
|
||||
<div id="id-token"></div>
|
||||
<script src="https://www.gstatic.com/firebasejs/5.5.3/firebase.js"></script>
|
||||
<script src="main.js"></script>
|
||||
</body>
|
||||
</html>
|
60
community/examples/firebase-jwt/app/main.js
Normal file
60
community/examples/firebase-jwt/app/main.js
Normal file
@ -0,0 +1,60 @@
|
||||
// Initialize Firebase
|
||||
var config = {
|
||||
apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
authDomain: "<your-app>.firebaseapp.com",
|
||||
databaseURL: "https://<your-app>.firebaseio.com",
|
||||
projectId: "<your-app>",
|
||||
storageBucket: "<your-app>.appspot.com",
|
||||
messagingSenderId: "xxxxxxxxxxxx"
|
||||
};
|
||||
firebase.initializeApp(config);
|
||||
|
||||
document.getElementById('login-form').onsubmit = function(event) {
|
||||
event.preventDefault();
|
||||
let email = document.getElementById('email').value;
|
||||
let pass = document.getElementById('password').value;
|
||||
login(email, pass);
|
||||
};
|
||||
|
||||
|
||||
document.getElementById('get-token').onclick = function(event) {
|
||||
event.preventDefault();
|
||||
firebase.auth().currentUser.getIdToken(true).
|
||||
then(token => document.getElementById('id-token').innerHTML = token);
|
||||
};
|
||||
|
||||
function login(email, password) {
|
||||
firebase.auth().signInWithEmailAndPassword(email, password)
|
||||
.then(function(user) {
|
||||
console.log('login success');
|
||||
})
|
||||
.catch(function(error) {
|
||||
// Handle Errors here.
|
||||
var errorCode = error.code;
|
||||
var errorMessage = error.message;
|
||||
console.log(error);
|
||||
});
|
||||
|
||||
let callback = null;
|
||||
let metadataRef = null;
|
||||
firebase.auth().onAuthStateChanged(user => {
|
||||
// Remove previous listener.
|
||||
if (callback) {
|
||||
metadataRef.off('value', callback);
|
||||
}
|
||||
// On user login add new listener.
|
||||
if (user) {
|
||||
// Check if refresh is required.
|
||||
metadataRef = firebase.database().ref('metadata/' + user.uid + '/refreshTime');
|
||||
callback = (snapshot) => {
|
||||
// Force refresh to pick up the latest custom claims changes.
|
||||
// Note this is always triggered on first call. Further optimization could be
|
||||
// added to avoid the initial trigger when the token is issued and already contains
|
||||
// the latest claims.
|
||||
user.getIdToken(true);
|
||||
};
|
||||
// Subscribe new listener to changes on that node.
|
||||
metadataRef.on('value', callback);
|
||||
}
|
||||
});
|
||||
}
|
41
community/examples/firebase-jwt/functions/index.js
Normal file
41
community/examples/firebase-jwt/functions/index.js
Normal file
@ -0,0 +1,41 @@
|
||||
const functions = require('firebase-functions');
|
||||
const admin = require('firebase-admin');
|
||||
admin.initializeApp(functions.config().firebase);
|
||||
|
||||
// On sign up.
|
||||
exports.processSignUp = functions.auth.user().onCreate(user => {
|
||||
console.log(user);
|
||||
// Check if user meets role criteria:
|
||||
// Your custom logic here: to decide what roles and other `x-hasura-*` should the user get
|
||||
let customClaims;
|
||||
if (user.email && user.email.indexOf('@hasura.io') != -1) {
|
||||
customClaims = {
|
||||
'https://hasura.io/jwt/claims': {
|
||||
'x-hasura-default-role': 'admin',
|
||||
'x-hasura-allowed-roles': ['user', 'admin'],
|
||||
'x-hasura-user-id': user.uid
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
customClaims = {
|
||||
'https://hasura.io/jwt/claims': {
|
||||
'x-hasura-default-role': 'user',
|
||||
'x-hasura-allowed-roles': ['user'],
|
||||
'x-hasura-user-id': user.uid
|
||||
}
|
||||
};
|
||||
}
|
||||
// Set custom user claims on this newly created user.
|
||||
return admin.auth().setCustomUserClaims(user.uid, customClaims)
|
||||
.then(() => {
|
||||
// Update real-time database to notify client to force refresh.
|
||||
const metadataRef = admin.database().ref("metadata/" + user.uid);
|
||||
// Set the refresh time to the current UTC timestamp.
|
||||
// This will be captured on the client to force a token refresh.
|
||||
return metadataRef.set({refreshTime: new Date().getTime()});
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
});
|
3896
community/examples/firebase-jwt/functions/package-lock.json
generated
Normal file
3896
community/examples/firebase-jwt/functions/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
16
community/examples/firebase-jwt/functions/package.json
Normal file
16
community/examples/firebase-jwt/functions/package.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "functions",
|
||||
"description": "Cloud Functions for Firebase",
|
||||
"scripts": {
|
||||
"serve": "firebase serve --only functions",
|
||||
"shell": "firebase functions:shell",
|
||||
"start": "npm run shell",
|
||||
"deploy": "firebase deploy --only functions",
|
||||
"logs": "firebase functions:log"
|
||||
},
|
||||
"dependencies": {
|
||||
"firebase-admin": "~6.0.0",
|
||||
"firebase-functions": "^2.0.3"
|
||||
},
|
||||
"private": true
|
||||
}
|
Loading…
Reference in New Issue
Block a user