How to integrate Hasura with Firebase Authentication - firebase-authentication

Does anyone here have experienced Hasura graphql ?
I started exploring #postgrest to fulfill my serverless needs, but I end up dumping it since its hard to make its authentication. Just last two day I learned that I can do phone number authentication with Firebase Authentication with my reactjs App.
Then I look back at my database solution, I learned that Apollo graphql and also Hasura may solve my database needs. I have no Idea whether I can integrate my phone auth so that It produce jwt and can be used in Hasura / Apollo maybe?
Cut story short, is Hasura famous among us here ? What do you guys use for Graphql authentication ? Can it integrate with Firebase authentication ?
any suggestions much appreciated!

I've integrated GraphQL + Firebase Auth with both frontend and backend system before. I didn't use Hasura, just a normal Apollo GraphQL, but the concept should be the same. Hope it can help
Frontend phone authentication
In this example the frontend was developed using Flutter.
Get firebaseToken from FirebaseAuth, passing to login Mutation with input firebaseToken. This login Mutation must return JWT token.
Store this JWT token securely in your app
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: phoneNumber,
verificationCompleted:
(PhoneAuthCredential credential) async {
final UserCredential cr =
await FirebaseAuth.instance
.signInWithCredential(credential);
final String firebaseToken =
await cr.user!.getIdToken();
final QueryResult qe = await runMutation(
{"firebaseToken": firebaseToken})
.networkResult!;
final String jwt =
Login$Mutation.fromJson(qe.data!)
.login
.jwtToken;
...
},
...
);
GraphQL
mutation Login($firebaseToken: String!) {
login(input: { firebaseToken: $firebaseToken }) {
jwtToken
}
}
Schema GQL
type Mutation {
login(input: LoginInput!): Login! }
input LoginInput {
firebaseToken: String!
}
type Login {
jwtToken: String!
}
Backend Auth Resolver
In this example, backend was developed using NestJS (node.js). First create Auth resolver with login Mutation that accept firebaseToken. This login Mutation need to verify Id Token (Using Firebase Admin SDK). After verify you can get decodedToken, either phone number or email depending on your Firebase project authentication settings. Use this info to create new user if not exist. Return JWT token using payload e:g {id: user.id}
#Mutation(() => LoginDTO)
async login(
#Args('input', { type: () => LoginInput }) input: LoginInput
): Promise<LoginDTO> {
const decodedToken = await this.firebaseAuth.app
.auth()
.verifyIdToken(input.firebaseToken);
const number = (
decodedToken.firebase.identities.phone[0] as string
).substring(1);
const user = await this.passengerService.findOrCreateUserWithMobileNumber(
number
);
const payload = { id: user.id };
return {
jwtToken: this.jwtService.sign(payload),
};
}

Related

Why isn't NextAuth jwt automatically saved in cookies?

I have two simple authentication callback functions "jwt" and "session" which check if the user object exists and create the session if so.
callbacks: {
jwt: async ({ token, user }) => {
if(user) {
token.id = user.id
}
return token
},
session: ({ session, token }) => {
if(token) {
session.id = token.id
}
return session
},
}
My issue is, and I have been searching a lot to find information concerning this, why isn't this jwt automatically saved to cookies?
I find that my session is created and I am successfully "logged in", however if I look into my local storage there are no jwt cookies saved.
Do I have to manually save the jwt to my cookies in the jwt callback? Or is the jwt cookie not even required in the case of jwt session strategy? I need jwt cookies because from what I've read about middleware most solutions use cookies and decrypt the jwt to see if the user is logged in, instead of checking the getSession() hook.
You might need to to explain your problem in more detail since I canĀ“t really tell what you already implemented and left out for simplicity sake. I hope this helps you anyway:
The steps to add the cookie look roughly like this:
Create / sign a jwt with a npm package like jose or jsonwebtoken
Set the header of your response and add your signed jwt to it; return it to the client
import { SignJWT, jwtVerify, JWTVerifyResult } from "jose";
async function setCookie(response, user: {id: number}) {
const token = await generateJwtToken(user);
response.setHeader("Set-Cookie", [
`user=${token};` +
"expires=" + new Date(new Date().getTime() + 1 * 86409000).toUTCString() + ";"
]);
response.status(200).json({message: "Successfully set cookie"})
}
async function generateJwtToken(user: { id: number }) {
return await new SignJWT({id: user.id})
.setProtectedHeader({ alg: "HS256" })
.setIssuedAt()
.setExpirationTime("24h")
.sign(new TextEncoder().encode(process.env.YOUR_JWT_TOKEN_ENV_VAR));
}
Verify the jwt on further requests with the same package as in 1.
export async function verifyJwt(request) {
const token = request.cookies["yourCustomUser"];
const verified: JWTVerifyResult = await jwtVerify(
token,
new TextEncoder().encode(process.env.YOUR_JWT_TOKEN_ENV_VAR),
);
verified.payload.status = 200;
return verified.payload;
}
In addition to that you might wanna add sameSite=Strict, secure or path=/. For further information you should have a look at developers.mozilla
Also make sure to add error handling for expired Jwts etc.

OctoKit with Auth0 (Github Login) in NextJS

I am building a Next JS app that has Github Login through Auth0 and uses the Octokit to fetch user info / repos.
In order to get the IDP I had to setup a management api in auth0. https://community.auth0.com/t/can-i-get-the-github-access-token/47237 which I have setup in my NodeJs server to hide the management api token as : GET /getaccesstoken endpoint
On the client side : /chooserepo page, I have the following code :
const chooserepo = (props) => {
const octokit = new Octokit({
auth: props.accessToken,
});
async function run() {
const res = await octokit.request("GET /user");
console.log("authenticated as ", res.data);
}
run();
And
export const getServerSideProps = withPageAuthRequired({
async getServerSideProps({ req, params }) {
let { user } = getSession(req);
console.log("user from get session ", user);
let url = "http://localhost:4000/getaccesstoken/" + user.sub;
let data = await fetch(url);
let resData = await data.text();
return {
props: { accessToken: resData }, // will be passed to the page component as props
};
},
});
However, I keep getting Bad credentials error. If I directly put the access token in the Octokit it seems to work well, but doesn't work when it's fetching the access token from the server.
It seems like Octokit instance is created before server side props are sent. How do I fix it ?
I figured out the error by comparing the difference between the request headers when hardcoding and fetching access token from server. Turns out quotes and backslashes need to be replaced (and aren't visible when just console logging)

Vue.js (Quasar) SPA restarts Auth Code Flow on each page reload

I have a SPA built with the Quasar Framework (based on Vue.js). The SPA is registered in Auth0 and uses the auth0-spa-js library to handle the login via Auth Code Flow. While the login works and I get a token, when I reload the page the Auth Code Flow is started again and the user is redirected to the /authorize endpoint to get a new code, which is then again exchanged for a new token.
To me this does not seem like the correct behaviour. I would have expected that the Auth0 library caches/stores the token in the browser and on page reload checks if there is a valid token already, instead of restarting the Auth Code Flow every time.
Or is that actually the way it should be considering this is a SPA and token storage in the browser is not good.
The code from the boot file:
import createAuth0Client from '#auth0/auth0-spa-js';
import axios from 'axios'
export default async ({ app, router, Vue }) => {
let auth0 = await createAuth0Client({
domain: '{domain}.auth0.com',
client_id: '{client_id}',
audience: '{audience}'
});
const isAuthenticated = await auth0.isAuthenticated();
if (isAuthenticated) {
// show the gated content
await afterLogin(auth0, Vue)
return;
}
const query = window.location.search;
if (query.includes("code=") && query.includes("state=")) {
// Process the login state
await auth0.handleRedirectCallback()
await afterLogin(auth0, Vue)
// Use replaceState to redirect the user away and remove the querystring parameters
window.history.replaceState({}, document.title, "/");
return
}
await auth0.loginWithRedirect({
redirect_uri: window.location.origin
});
}
async function afterLogin(auth0, Vue) {
let user = await auth0.getUser()
Vue.prototype.$user = user
Vue.prototype.$auth = auth0
// let claims = await auth0.getIdTokenClaims()
// console.log(claims)
// setAuthHeader(claims.__raw)
let token = await auth0.getTokenSilently()
setAuthHeader(token)
}
function setAuthHeader(token) {
axios.defaults.headers.common['Authorization'] = 'Bearer ' + token
}
What am I missing?
When refreshing you should check if user exists..
const user = await auth0.getUser();
You could create an autoCheckUser function at src/boot folder, that can check if user exist every time app created...
The feedback from Auth0 is that this is the expected behaviour.
"If the token is stored in memory, then it will be erased when the page is refreshed. A new token is silently requested via a cookie session."
Here is the link to the original answer

Express auth middleware not liking the JWT Token it receives from client side React Native AsyncStorage

I'm working on login authentication with a react native app. I have login working as someone logs in for the first time through the login form. At this initial login step, I'm sending my JWT web token to my secure routes in my express server as a string and this works fine. NOTE: I have a middleware setup that my token goes through before my routes will fire completely.
The problem is when the app refreshes it needs to go to my "local storage". My express middleware doesn't like the token I'm pulling from my "local storage (I retrieve it through AsyncStorate.getItem('UserToken'). When I look at the item being stored in the header, it seems like it's the exact item I was sending it at the initial login, but I think it's my middleware on the server that doesn't like the value when it's coming from the " local storage" header. Below is my middlware.js code.
I've tried looking at the JWT value being sent in both scenarios, and it seems like it's the exact item being sent to the server in both situations.
module.exports = function(req, res, next) {
//Get Token from header
const token = req.header('x-auth-token');
// Check if no token
if(!token) {
return res.status(401).json({ msg: 'No token, authorization denied'})
}
//Verify token
try {
var decoded = jwt.verify(token, global.gConfig.jwtSecret);
req.user = decoded.user;
next();
}catch(err){
res.status(401).json({ msg: 'Token is not valid'});
}
}
This is the function I'm using to retrieve the token from AsyncStorage
export const getAsyncStorage = async () => {
try {
// pulling from header to get x-auth-token here
Value = await AsyncStorage.getItem('Usertoken');
//setting x-auth-token here
axios.defaults.headers.common['x-auth-token'] = Value;
} catch (error) {
// Error retrieving data
}
};

Is hapi-auth-bearer-token for API authentication only?

I've been reading the docs on the bearer token module for hapi and wondering if it is suitable for only API authentication or for general purpose web application authentication as well.
A few things are not clear from the docs.
1) From where does the token originate? IOW, what mechanism creates the token?
2) By what means does the user login in the first place?
Thanks
I've been reading the docs on the bearer token module for hapi and wondering if it is suitable for only API authentication or for general purpose web application authentication as well.
Bearer authentication is used to authorize API requests.
1) From where does the token originate? IOW, what mechanism creates the token?
You need to generate and validate the tokens on your own.
To generate, store and validate tokens, you can use uuid and bcryptjs (tokens are sent to the user and hashed tokens are saved in a database for later validation).
The following examples are in TypeScript.
import uuid from 'uuid';
import bcrypt from 'bcryptjs';
export interface GeneratedToken {
token: string;
hashedToken: string;
}
const generateToken = async function(): Promise<GeneratedToken> {
return new Promise<GeneratedToken>((resolve, reject) => {
let token = uuid.v4().replace(/-/g, '');
bcrypt.genSalt(10, function(error, salt) {
if (error) {
reject(error);
} else {
bcrypt.hash(token, salt, function(error, hashedToken) {
if (error) {
reject(error);
} else {
resolve({
token: token,
hashedToken: hashedToken
});
}
});
}
});
});
};
const validateToken = function(token: string, hashedToken: string): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
bcrypt.compare(token, hashedToken, function(error, result) {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
};
2) By what means does the user login in the first place?
You need to use another authentication scheme which can be as simple as storing hashed passwords in your database and comparing them with the data sent to your API route. The same hashing scheme used for the tokens works just fine.