Correct implementation of refreshtoken with ApolloClient - reactjs

I am trying to refresh the authentication token when it is near the expiration time, however I end up with an endless loop. The expectation is that the code that checks the expiration time in App.js should work and reset the auth token and the refresh token, however is starts looping endlessly.
I have the following Auth helper functions:
const jwt = require("jsonwebtoken");
const User = require("../models/User");
const { AuthenticationError } = require("apollo-server-express");
const config = require("config");
const jwtSecret = config.get("jwt_secret");
const jwtRefreshTokenSecret = config.get("jwt_refresh_token");
const authFunctions = {
checkSignedIn: async (req, requireAuth = true) => {
const header = req.headers.authorization;
if (header) {
const token = header.replace("Bearer ", "");
const decoded = jwt.verify(token, jwtSecret);
console.log(decoded);
let user = await User.findById(decoded.id);
if (!user) {
throw new AuthenticationError("Invalid user credentials.");
}
return user;
}
if (requireAuth) {
throw new AuthenticationError("You must be logged in.");
}
return null;
},
issueToken: async (user) => {
let token = "Bearer " + (await authFunctions.createToken(user));
let refreshToken = await authFunctions.createToken(user, 100);
return { token, refreshToken };
},
issueNewToken: async (req) => {
try {
const token = req.headers.refreshtoken;
if (token) {
const decoded = await jwt.verify(token, jwtRefreshTokenSecret);
let user = await User.findById(decoded.id);
console.log(user);
if (!user) {
throw new AuthenticationError("No user found.");
}
let tokens = await authFunctions.issueToken(user);
return { ...tokens, user };
}
} catch (err) {
throw new AuthenticationError("Invalid Refresh Token.");
}
},
createToken: async ({ id, address }, expiresIn = 60) => {
let secret = expiresIn === 60 ? jwtSecret : jwtRefreshTokenSecret;
return await jwt.sign({ id, address }, secret, { expiresIn });
},
};
module.exports = authFunctions;
The schema:
const { gql } = require("apollo-server");
const typeDefs = gql`
scalar Date
scalar MongoId
type User {
_id: MongoId!
address: String!
createdAt: Date
}
type Auth {
user: User!
token: String!
refreshToken: String!
}
input LoginInput {
address: String!
}
type Query {
refreshTokens: Auth!
}
type Mutation {
createOrGetUser(loginInput: LoginInput): Auth!
}
`;
module.exports = typeDefs;
The resolvers:
const User = require("../../models/User");
const {
issueToken,
issueNewToken,
checkSignedIn,
} = require("../../middleware/Auth");
const resolvers = {
Query: {
refreshTokens: async (root, args, { req }, info) =>
await issueNewToken(req),
},
Mutation: {
createOrGetUser: async (root, args, { req }, info) => {
try {
const existingUser = await User.findOne({
address: args.loginInput.address,
})
.populate({
path: "orders",
model: Order,
})
.exec();
if (existingUser) {
let tokens = await issueToken(existingUser);
return {
user: existingUser,
...tokens,
};
}
const user = await new User({
address: args.loginInput.address,
});
const result = await user.save();
let tokens = await issueToken(result);
return {
user,
...tokens,
};
} catch (err) {
throw err;
}
},
},
};
module.exports = resolvers;
And the App.js
import React, { Fragment } from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import {
ApolloClient,
ApolloLink,
createHttpLink,
useReactiveVar,
concat,
} from "#apollo/client";
import { ApolloProvider } from "#apollo/client/react";
import { accessTokenVar, isLoggedInVar } from "./cache";
import cache from "./cache.js";
import jwtDecode from "jwt-decode";
// Styling
import "./styles/App.css";
// Components
import Routes from "./components/routing/Routes";
import Navbar from "./components/navbar/Navbar";
import { REFRESH_TOKENS } from "./queries/User";
const httpLink = createHttpLink({
uri: "/graphql",
});
const refreshTokens = () => {
return client.query({ query: REFRESH_TOKENS }).then((response) => {
console.log(response);
// Need to set the token to cache here, but the query doesn't get executed it seems
//accessTokenVar(response.data)
return;
});
};
const authMiddleware = new ApolloLink((operation, forward) => {
// add the authorization to the headers
let token = accessTokenVar();
if (token) {
token = token.replace("Bearer ", "");
const { exp } = jwtDecode(token);
console.log(exp);
// Refresh the token a minute early to avoid latency issues
const expirationTime = exp - 30;
if (Date.now() / 1000 >= expirationTime) {
refreshTokens(); // this keeps going on a loop
}
}
operation.setContext(({ headers = {} }) => ({
headers: {
...headers,
authorization: token ? token : "",
},
}));
return forward(operation);
});
const client = new ApolloClient({
cache,
link: concat(authMiddleware, httpLink),
connectToDevTools: true,
credentials: "include",
});
const App = () => {
const accessToken = useReactiveVar(accessTokenVar);
const isLoggedIn = useReactiveVar(isLoggedInVar);
//let isLoggedIn;
accessToken ? isLoggedInVar(true) : isLoggedInVar(false);
return (
<ApolloProvider client={client}>
<Router>
<Fragment>
<Navbar isLoggedIn={isLoggedIn} />
</Fragment>
</Router>
</ApolloProvider>
);
};
export default App;
Note: When createOrGetUser is executed the first time, the flow works. Then, I wait for 30 seconds and send a protected query, after which it gets in an endless loop.
Overall, I feel this flow is broken somehow, but I can't figure out what exactly. Would appreciate any help with this!

Related

How to refresh firebase access token

In the current project, I log in to Firebase and get the user's information.
However, the log below occurs on the server, and there is a problem of getting all user information, not a specific user.
{"level":30,"time":1675750089706,"pid":16748,"hostname":"DESKTOP-JP9RKDH","msg":"HTTP GET: /api/friends/"}
{"level":30,"time":1675750089707,"pid":16748,"hostname":"DESKTOP-JP9RKDH","msg":"UserID is invalid, retrieving all friends"}
{"level":30,"time":1675750089733,"pid":16748,"hostname":"DESKTOP-JP9RKDH","msg":"Decoded Token User ID: Yk1eA8Vbh7fFIRd3eTNXvyHCdwH3"}
I thought there was no problem because I was refreshing when the token expired as follows.
Also, checking the token stored in the cookie every hour showed that it was a new token.
Please let me know what is causing this error.
const setToken = token => {
cookie.set('FB_TOKEN', token);
};
export const getToken = () => {
fbAuth.onIdTokenChanged(async user => {
if (user) {
const newToken = await user.getIdToken();
setToken(newToken);
}
});
const token = cookie.get('FB_TOKEN') ?? '';
return token;
};
export const login = createAsyncThunk('user/login', async (data, { rejectWithValue }) => {
try {
let credential;
if (data.type === 'google') {
localStorage.clear();
const provider = new GoogleAuthProvider();
credential = await signInWithPopup(fbAuth, provider);
const token = await credential.user.getIdToken();
setToken(token);
} else {
credential = await signInWithEmailAndPassword(fbAuth, data.loginInfo.email, data.loginInfo.password);
const token = await credential.user.getIdToken();
setToken(token);
}
return {
id: credential.user.uid,
nickname: credential.user.displayName,
email: credential.user.email,
image: credential.user.photoURL,
};
} catch (error) {
return rejectWithValue(error.response.data);
}
});
axios.defaults.baseURL = backendUrl;
axios.defaults.withCredentials = true;
axios.defaults.headers.post['Content-Type'] = 'application/json';
axios.interceptors.request.use(
async config => {
const token = await getToken();
config.headers.Authorization = `Bearer ${token}`;
return config;
},
error => {
return Promise.reject(error);
},
);
export const loadMyFriends = createAsyncThunk('schedule/loadMyFriends', async () => {
const response = await axios.get('/friends');
return response.data;
});

Next.js | HOC from REST API with Typescript not being read when wrapping a child component to access it's data

I have this HOC line of code from withUser.tsx. When a user is authenticated, the authenticated pages will then be wrapped by it so that the specified user-role will be the one to only have access to pages intended.
import axios, { AxiosError } from "axios";
import { API } from "../config";
import { getCookie } from "../helpers/auth";
const withUser = (Page: any) => {
const WithAuthUser = (props: any): JSX.Element => <Page {...props} />;
WithAuthUser.getInitialProps = async (context: any): Promise<any> => {
const token = getCookie("token", context.req);
let user = null;
let userLinks = [];
if (token) {
try {
const response = await axios.get(`${API}/user`, {
headers: {
authorization: `Bearer ${token}`,
contentType: "application/json",
},
});
console.log("Response in withUser: ", response);
user = response.data.user;
userLinks = response.data.links;
} catch (err: unknown) {
const error = err as AxiosError;
if (error.response?.status === 401) {
user = null;
}
}
}
if (user === null) {
// redirect
context.res.writeHead(302, {
Location: "/",
});
context.res.end();
} else {
return {
...(Page.getInitialProps ? await Page.getInitialProps(context) : {}),
user,
token,
userLinks,
};
}
};
return WithAuthUser;
};
export default withUser;
Now, the above code is not my final writing of TypeScript, I could be wrong but this is how I converted it from JS, please feel free to give me a refactored TSX codes here, here is the JS version:
import axios from "axios";
import { API } from "../config";
import { getCookie } from "../helpers/auth";
const withUser = (Page) => {
const WithAuthUser = (props) => <Page {...props} />;
WithAuthUser.getInitialProps = async (context) => {
const token = getCookie("token", context.req);
let user = null;
let userLinks = [];
if (token) {
try {
const response = await axios.get(`${API}/user`, {
headers: {
authorization: `Bearer ${token}`,
contentType: "application/json",
},
});
console.log("Response in withUser: ", response);
user = response.data.user;
userLinks = response.data.links;
} catch (error) {
if (error.response.status === 401) {
user = null;
}
}
}
if (user === null) {
// redirect
context.res.writeHead(302, {
Location: "/",
});
context.res.end();
} else {
return {
...(Page.getInitialProps ? await Page.getInitialProps(context) : {}),
user,
token,
userLinks,
};
}
};
return WithAuthUser;
};
export default withUser;
But now, when using it when an Authenticated /user page, I could not get any data from the user. It will give me an undefined and for example, user.first_name will not be shown:
import withUser from "../withUser";
const User = ({ user }: any): JSX.Element => (
<div className="flex min-h-screen flex-col items-center justify-center">
{user.first_name}
</div>
);
export default withUser(User);
Any correct ways of implementing this would be very much appreciated. Thanks!
Whether you are in Reactjs or Nextjs, I think there needs to have a correct type definitions of your HOC component in the first place.
First you need to define your HOC component as a React.ComponentType:
const withUser = (ChildComp: React.ComponentType<any | string>) => { /* code follows */ }
you also need to define an interface for the expected values for these"
const token = getCookie("token", context.req);
let user = null;
let userLinks = [];
and when you wrap your child component, say user.tsx, do it like this:
type UserType = {
first_name: string
}
const User: React.SFC<ContainerProps> = ({ user}: UserType)=> (
<h1>{user.first_name ?? "User not found"}</h1>
);
export default withUser(User);
You can read more about here: Create a TypeScript HOC in React
Okay, sorry this was just a bug and I figure out that I did not have any userLinks from the REST API that I was passing in. So I can already consider this question as resolved as I have already fixed it.
Here is the code of my fix:
import axios, { AxiosError } from "axios";
import { API } from "../config";
import { getCookie } from "../helpers/auth";
const withUser = (Page: any) => {
const WithAuthUser = (props: any): JSX.Element => <Page {...props} />;
WithAuthUser.getInitialProps = async (context: any): Promise<any> => {
const token = getCookie("token", context.req);
console.log("token: ", token);
let user = null;
if (token) {
try {
const response = await axios.get(`${API}/user`, {
headers: {
authorization: `Bearer ${token}`,
contentType: "application/json",
},
});
console.log("response: ", response);
user = response.data;
} catch (err: unknown) {
const error = err as AxiosError;
if (error.response?.status === 401) {
user = null;
}
}
}
if (user === null) {
// redirect
context.res.writeHead(302, {
Location: "/",
});
context.res.end();
} else {
return {
...(Page.getInitialProps ? await Page.getInitialProps(context) : {}),
user,
token,
};
}
}
return WithAuthUser;
}
export default withUser;

How do I get the data from an API call, in a different file, in React

I'm trying to update some code, taking into account new sdk versions. I have the new api call in one file:
import { CognitoIdentityProviderClient, ListUsersCommand } from "#aws-sdk/client-cognito-identity-provider";
import awsmobile from "../../aws-exports";
import { Auth } from "aws-amplify";
export default async function ListUsers() {
await Auth.currentCredentials().then((data) => {
const client = new CognitoIdentityProviderClient({
region: awsmobile.aws_project_region,
credentials: data
});
const params = {
UserPoolId: awsmobile.aws_user_pools_id
};
const command = new ListUsersCommand(params);
client.send(command).then(
(data) => {
return data
},
(error) => {
console.log(error)
}
);
});
}
I'm trying to retrive the data in another file:
import ListUsers from "../../../API/cognito/ListUsers";
import ListUsersInGroup from "../../../API/cognito/ListUsersInGroup";
import { useState, useEffect, useRef } from "react";
import PortalUsersTable from "../../../components/tables/PortalUsersTable";
export default function ManageUsers() {
const [userDetails, setUserDetails] = useState("");
const refUsers = useRef();
const refUsersExec = useRef();
const refUsersAdmin = useRef();
const refUsersGroups = useRef();
useEffect(() => {
function getUsers() {
refUsers.current = ListUsers();
refUsersExec.current = ListUsersInGroup("usersAdmin");
refUsersAdmin.current = ListUsersInGroup("usersExec");
//setUsersTloOfficers(apiTloOfficers);
refUsersGroups.current = ListUsersInGroup("usersGroups");
let userData = [];
let arrUsersExec = [];
for (let a in refUsersExec.current.Users) {
arrUsersExec.push(refUsersExec.current.Users[a].Username);
}
let arrUsersAdmin = [];
for (let b in refUsersAdmin.current.Users) {
arrUsersAdmin.push(refUsersAdmin.current.Users[b].Username);
}
let arrUsersGroups = [];
for (let b in refUsersNtigGroups.current.Users) {
arrUsersGroups.push(refUsersGroups.current.Users[b].Username);
}
for (let i in refUsers.current.Users) {
let email = null;
for (let x in refUsers.current.Users[i].Attributes) {
if (refUsers.current.Users[i].Attributes[x].Name === "email") {
email = refUsers.current.Users[i].Attributes[x].Value;
break;
}
}
let memberExec = arrUsersExec.includes(refUsers.current.Users[i].Username);
let memberAdmin = arrUsersAdmin.includes(refUsers.current.Users[i].Username);
let memberGroups = arrUsersGroups.includes(refUsers.current.Users[i].Username);
userData.push({
id: i,
Username: refUsers.current.Users[i].Username,
AccountStatus: refUsers.current.Users[i].UserStatus,
Email: email,
Users: memberGroups,
Exec: memberExec,
Admin: memberAdmin,
});
}
setUserDetails(userData);
}
getUsers();
}, []);
return (
<>
<h2>Manage Portal Users</h2>
<PortalUsersTable userDetails={userDetails} />
</>
);
}
The logic to handle the API data is sound.
This is the old API call:
import AWS from "aws-sdk";
import awsmobile from "../../aws-exports";
import { Auth } from "aws-amplify";
export default async function ListUsers() {
let idToken = "";
await Auth.currentAuthenticatedUser().then((user) => {
idToken = user.signInUserSession.idToken.getJwtToken();
});
AWS.config.region = awsmobile.aws_cognito_region;
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: awsmobile.aws_cognito_identity_pool_id,
RoleArn: "arn:aws:iam::xxxxxxxxx:role/xxxxxxxxxxxxx",
Logins: { "xxxxxxxxxxxxxxxxxxxx": idToken }
});
let cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
let params = {
UserPoolId: awsmobile.aws_user_pools_id,
AttributesToGet: ["email"]
};
return new Promise((resolve, reject) => {
cognitoidentityserviceprovider.listUsers(params, function (err, result) {
if (err) {
console.log(err);
//onError(err);
reject(err);
return;
}
if (result) {
resolve(result);
}
});
});
}
I can see the new API call is returning the correct data in the console. I think I'm not passing the data between files correctly.
I've tried various ways of changing the API call function, reading the cognito sdk description but it's not the API call that is incorrect.
How can I use the API call data in the separate file?
Even if your API call if correct, it looks like you are not returning anything from your function ListUsers. You are mixing async/await pattern with the then. I assume you have added a console.log right before the return data. Refactoring your function using async/await would look like this :
export default async function ListUsers() {
try {
const data = await Auth.currentCredentials();
const client = new CognitoIdentityProviderClient({
region: awsmobile.aws_project_region,
credentials: data,
});
const params = {
UserPoolId: awsmobile.aws_user_pools_id,
};
const command = new ListUsersCommand(params);
const commandData = await client.send(command);
return commandData;
} catch (error) {
console.log(error);
}
}

MS Graph API issue with React

I'm working on a React project.
I'm able to sign in in the graph API, also able to get the user's contacts, but, not able to get his calendar events.
the config file:
export const msalConfig = {
auth: {
clientId: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
authority: "https://login.microsoftonline.com/common",
redirectUri: "http://localhost:4200",
},
cache: {
cacheLocation: "sessionStorage", // This configures where your cache will be stored
storeAuthStateInCookie: true, // Set this to "true" if you are having issues on IE11 or Edge
}
};
// Add scopes here for ID token to be used at Microsoft identity platform endpoints.
export const loginRequest = {
scopes: ["Calendars.ReadWrite", "Contacts.Read"]
};
// Add the endpoints here for Microsoft Graph API services you'd like to use.
export const graphConfig = {
graphMeEndpoint: "https://graph.microsoft.com/v1.0/me",
graphCalendarsEndpoint: "https://graph.microsoft.com/v1.0/me/events",
graphContactsEndpoint: "https://graph.microsoft.com/v1.0/me/contacts",
};
1/ The working part
the importContact.jsx file:
...
import { loginRequest, graphConfig } from '../authConfig.jsx';
import { useMsal } from "#azure/msal-react";
const ImportContacts = (props) => {
const { instance, accounts, inProgress } = useMsal();
const [accessToken, setAccessToken] = useState(null);
const [graphData, setGraphData] = useState(null);
const getMicrosoftContacts = () => {
const request = {
...loginRequest,
account: accounts[0]
};
var contacts= [];
// Silently acquires an access token which is then attached to a request for Microsoft Graph data
instance.acquireTokenSilent(request).then((response) => {
console.log('acquireTokenSilent')
setAccessToken(response.accessToken);
callMsGraph(response.accessToken).then(
response => {
setGraphData(response);
console.log(response)
for (const item of response.value){
contacts.push({
provider: 'Microsoft',
id: item.id,
email: item.emailAddresses[0].address,
firstName: item.givenName,
lastName: item.surname,
name: item.displayName,
label:item.displayName + " (" + item.emailAddresses[0].address + ")" }
)
}
setItems(contacts);
}
);
}).catch((e) => {
instance.acquireTokenPopup(request).then((response) => {
setAccessToken(response.accessToken);
callMsGraph(response.accessToken).then(
response => {
setGraphData(response);
for (const item of response.value){
contacts.push({
provider: 'Microsoft',
id: item.id,
email: item.emailAddresses[0].address,
firstName: item.givenName,
lastName: item.surname,
name: item.displayName,
label:item.displayName }
)
}
setItems(contacts);
}
);
});
});
}
async function callMsGraph(accessToken) {
const headers = new Headers();
const bearer = `Bearer ${accessToken}`;
headers.append("Authorization", bearer);
const options = {
method: "GET",
headers: headers
};
return fetch(graphConfig.graphContactsEndpoint, options)
.then(response => response.json())
.catch(error => console.log(error));
}
...
}
2/ The non working part in events.jsx file:
...
import { loginRequest, graphConfig } from '../authConfig.jsx';
import { useMsal } from "#azure/msal-react";
class Events extends Component {
constructor(props) {
super(props);
this.getMicrosoftEvents = this.getMicrosoftEvents.bind(this);
}
componentDidMount(){
var date = new Date();
var firstDay = new Date(date.getFullYear(), date.getMonth(), 1);
var lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0);
this.getMicrosoftEvents(firstDay, lastDay);
}
getMicrosoftEvents(start, end) {
console.log('log displayed in console')
const { instance, accounts, inProgress } = useMsal();
const [accessToken, setAccessToken] = useState(null);
const [graphData, setGraphData] = useState(null);
console.log('log not displayed in console')
const request = {
...loginRequest,
account: accounts[0]
};
// Silently acquires an access token which is then attached to a request for Microsoft Graph data
instance.acquireTokenSilent(request).then((response) => {
console.log('acquireTokenSilent')
setAccessToken(response.accessToken);
callMsGraph(response.accessToken, start, end).then(
response => {
console.log('microsoft response ' + response)
}
);
}).catch((e) => {
console.log('microsoft response error ' + e)
instance.acquireTokenPopup(request).then((response) => {
setAccessToken(response.accessToken);
callMsGraph(response.accessToken, start, end).then(
response => {
console.log('microsoft response ' + response)
}
);
});
});
async function callMsGraph(accessToken, start, end) {
console.log('callMsGraph ')
const headers = new Headers();
const bearer = `Bearer ${accessToken}`;
headers.append("Authorization", bearer);
console.log('Authorization ' + bearer)
const options = {
method: "GET",
headers: headers
};
return fetch(graphConfig.graphCalendarsEndpoint
+ '?startDateTime='
+ start
+ '&endDateTime='
+ end,
options)
.then(response => {
console.log('microsoft response ' + response.json())
response.json();
})
.catch(error => console.log(error));
}
}
I'm not getting any error, the api call is not made, the difference between the two calls is one is made after a button click, and the other on load.
Adding logs showed me that the problem could be in this line as the logs are not displayed after it:
const { instance, accounts, inProgress } = useMsal();
What am I doing wrong?
In events.jsx Events component is a class component and you are calling useMsal() and useState() hooks in getMicrosoftEvents. That will not work because hooks can only be called in function components.
You need to make Events component functional like ImportContacts.
Instead of
class Events extends Component {
...
Do this
const Events= (props) => {
const { instance, accounts, inProgress } = useMsal();
const [accessToken, setAccessToken] = useState(null);
const [graphData, setGraphData] = useState(null);
...

react firebase authentication with apollo graphql

I have found a great article adding authentication into react.
Article: https://www.robinwieruch.de/complete-firebase-authentication-react-tutorial/
This article finishes the firebase setup (before redux) with a HOC component that I can put into the app and can access with context.
My issue is how do i put this into the apollo client which is outside of the app component so even with the context I cant set it. I have this same problem with redux. Only one I have found is use local storage but I would like to avoid that.
This is my apollo client in the main app component.
const client = new ApolloClient({
uri: clientUrl,
request: async operation => {
const token = How_do_i_set_this <-- ???
console.log('token in request', token)
operation.setContext({
headers: {
authorization: token ? `Bearer ${token}` : ''
}
});
}
});
const App = () => (
<DashAppHolder>
<ApolloProvider client={client}>
<Provider store={store}>
<PublicRoutes history={history} />
</Provider>
</ApolloProvider>
</DashAppHolder>
);
So this my not be the best way but this is how I have it setup at the moment.
this is my apollo client setup
import Firebase from './helpers/firebase';
import ApolloClient from "apollo-boost";
const client = new ApolloClient({
uri: clientUrl,
request: async operation => {
const fireToken = await Firebase.token();
console.log('fire token', fireToken);
operation.setContext({
headers: {
authorization: fireToken ? `Bearer ${fireToken}` : 'token bad'
}
});
}
});
This is my firebase helper class I import
import firebase from 'firebase';
import 'firebase/firestore';
import { firebaseConfig } from '../../settings';
const valid = firebaseConfig && firebaseConfig.apiKey && firebaseConfig.projectId;
const firebaseApp = firebase.initializeApp(firebaseConfig);
const firebaseAuth = firebase.auth;
class FirebaseHelper {
isValid = valid;
EMAIL = 'email';
FACEBOOK = 'facebook';
GOOGLE = 'google';
GITHUB = 'github';
TWITTER = 'twitter';
constructor() {
this.login = this.login.bind(this);
this.logout = this.logout.bind(this);
this.signup = this.signup.bind(this);
this.resetPassword = this.resetPassword.bind(this);
this.doPasswordUpdate = this.doPasswordUpdate.bind(this);
this.auth = this.auth.bind(this);
this.database = firebase.firestore();
}
token = async() => {
const user = this.user()
if (user) {
return await user.getIdToken().then(token => { return token });
} else {
return null;
}
}
user() {
return firebaseAuth().currentUser;
}
login(provider, info) {
switch (provider) {
case this.EMAIL:
return firebaseAuth().signInWithEmailAndPassword(
info.email,
info.password
);
case this.GOOGLE:
var googleProvider = new firebase.auth.GoogleAuthProvider();
return firebaseAuth().signInWithPopup(googleProvider);
default:
}
}
signup(provider, info) {
switch (provider) {
case this.EMAIL:
return firebaseAuth().createUserWithEmailAndPassword(
info.email,
info.password
);
case this.FACEBOOK:
return firebaseAuth().FacebookAuthProvider();
case this.GOOGLE:
return firebaseAuth().GoogleAuthProvider();
case this.GITHUB:
return firebaseAuth().GithubAuthProvider();
case this.TWITTER:
return firebaseAuth().TwitterAuthProvider();
default:
alert('defaulted');
}
}
logout() {
return firebaseAuth().signOut();
}
auth = () => {
return firebaseAuth()
}
resetPassword(email) {
return firebaseAuth().sendPasswordResetEmail(email);
}
doPasswordUpdate(password) {
firebaseAuth().currentUser.updatePassword(password);
}
createNewRef() {
return firebase
.database()
.ref()
.push().key;
}
}
And lastly on the server here is my graphql setup relevent information I use
const admin = require('firebase-admin');
const getUid = async (request) => {
let idToken = (request.headers && request.headers.authorization) ? request.headers.authorization : null;
if (!idToken) {
console.log('no token found');
return null;
}
console.log('raw token', idToken);
var newToken = idToken.replace("Bearer ", "");
console.log('pure token', newToken)
let uid = await admin.auth().verifyIdToken(newToken)
.then(decodedToken => {
var uid = decodedToken.uid;
return uid;
}).catch((error) => {
// Handle error
console.log('uid failed', error);
return null;
});
console.log('uid found', uid);
return uid;
}
app.use(
'/graphql',
cors(),
express.json(),
graphqlExpress(async (request) => ({
schema: schema,
context: {
request: request,
uid: await getUid(request),
accountLoader: new Dataloader(keys => batchAccounts(keys, db)),
dealLoader: new Dataloader(keys => batchDeals(keys, db)),
}
})),
);
This answer works and gets me the UID of the user authorized through firebase and looks like there is no delay at all. This might not be the best answer because I Put this together from several documents when I was just learning everything and have been meaning to go back and revisit when I have time but again, working.

Resources