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),
};
}