Skip to content
8 changes: 8 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ jobs:
NEXT_PUBLIC_FIREBASE_PROJECT_ID: ${{secrets.FIREBASE_PROJECT_ID}}
NEXT_PUBLIC_ALGOLIA_APP_ID: ${{secrets.ALGOLIA_APP_ID}}
NEXT_PUBLIC_ALGOLIA_SEARCH_KEY: ${{secrets.ALGOLIA_SEARCH_KEY}}
FIREBASE_ADMIN_PRIVATE_KEY: ${{secrets.FIREBASE_ADMIN_PRIVATE_KEY}}
FIREBASE_ADMIN_CLIENT_EMAIL: ${{secrets.FIREBASE_ADMIN_CLIENT_EMAIL}}
FIREBASE_ADMIN_PRIVATE_KEY_ID: ${{secrets.FIREBASE_ADMIN_PRIVATE_KEY_ID}}
FIREBASE_ADMIN_CLIENT_ID: ${{secrets.FIREBASE_ADMIN_CLIENT_ID}}
FIREBASE_ADMIN_URI: ${{secrets.FIREBASE_ADMIN_URI}}
FIREBASE_ADMIN_TOKEN_URI: ${{secrets.FIREBASE_ADMIN_TOKEN_URI}}
FIREBASE_ADMIN_CERT_PROVIDER_URL: ${{secrets.FIREBASE_ADMIN_CERT_PROVIDER_URL}}
FIREBASE_ADMIN_CERT: ${{secrets.FIREBASE_ADMIN_CERT}}
deploy:
needs: lint-and-build
runs-on: ubuntu-latest
Expand Down
10 changes: 9 additions & 1 deletion .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,12 @@ jobs:
NEXT_PUBLIC_FIREBASE_API_KEY: ${{secrets.FIREBASE_API_KEY}}
NEXT_PUBLIC_FIREBASE_PROJECT_ID: ${{secrets.FIREBASE_PROJECT_ID}}
NEXT_PUBLIC_ALGOLIA_APP_ID: ${{secrets.ALGOLIA_APP_ID}}
NEXT_PUBLIC_ALGOLIA_SEARCH_KEY: ${{secrets.ALGOLIA_SEARCH_KEY}}
NEXT_PUBLIC_ALGOLIA_SEARCH_KEY: ${{secrets.ALGOLIA_SEARCH_KEY}}
FIREBASE_ADMIN_PRIVATE_KEY: ${{secrets.FIREBASE_ADMIN_PRIVATE_KEY}}
FIREBASE_ADMIN_CLIENT_EMAIL: ${{secrets.FIREBASE_ADMIN_CLIENT_EMAIL}}
FIREBASE_ADMIN_PRIVATE_KEY_ID: ${{secrets.FIREBASE_ADMIN_PRIVATE_KEY_ID}}
FIREBASE_ADMIN_CLIENT_ID: ${{secrets.FIREBASE_ADMIN_CLIENT_ID}}
FIREBASE_ADMIN_URI: ${{secrets.FIREBASE_ADMIN_URI}}
FIREBASE_ADMIN_TOKEN_URI: ${{secrets.FIREBASE_ADMIN_TOKEN_URI}}
FIREBASE_ADMIN_CERT_PROVIDER_URL: ${{secrets.FIREBASE_ADMIN_CERT_PROVIDER_URL}}
FIREBASE_ADMIN_CERT: ${{secrets.FIREBASE_ADMIN_CERT}}
96 changes: 0 additions & 96 deletions pages/api/silentLogin.js

This file was deleted.

73 changes: 51 additions & 22 deletions utils/authentication/authentication.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,70 @@
import client from '../axios';
import cookie from 'cookie';
import admin from '@utils/admin-firebase';
import AuthError from '@api/error/authError';
import { deserializeFirestoreTimestampToUnixTimestampNode } from '@utils/firebase/deserializerNode';

/**
* Checks if a user is authenticated or not.
* req: The request from getServerSideProps
* res: The response from getServerSideProps
*/
export async function isAuthenticated(req, res) {
const cookies = cookie.parse(req.headers.cookie || '');
const sessionCookie = cookies.session || '';
// Verify the session cookie. In this case an additional check is added to detect
// if the user's Firebase session was revoked, user deleted/disabled, etc.
try {
const response = await client.get('/api/silentLogin', {
headers: {
cookie: req.headers.cookie,
let decodedClaims = await admin.auth().verifySessionCookie(sessionCookie, true /** checkRevoked */);
let currentUser = await admin.auth().getUser(decodedClaims.uid);
let user = await getUser(currentUser.customClaims, currentUser.uid);

// Checking for user type from 3 different sources because Cloud function doesnt update the claims fast enough.
const donor = decodedClaims?.donor || user?.donor || currentUser?.customClaims?.donor || false;
const npo = decodedClaims?.npo || user?.npo || currentUser?.customClaims?.npo || false;
const isClaimSet = currentUser?.customClaims?.donor || currentUser?.customClaims?.npo || false;

let data = {
user: {
...user,
donor: donor,
npo: npo,
emailVerified: currentUser.emailVerified,
email: decodedClaims.email,
isClaimSet: isClaimSet,
},
});
return response.data;
};
deserializeFirestoreTimestampToUnixTimestampNode(data);
return data;
} catch (error) {
console.error(error.message);
console.error('silentLogin', error);
return null;
}
}

/**
* Checks if a user is authenticated or not. If not, route them back to login page.
* req: The request from getServerSideProps
* res: The response from getServerSideProps
*/
export async function isAuthenticatedFailureRouteBackToLogin(req, res) {
async function getUser(decodedClaims, uid) {
try {
const response = await client.get('/api/silentLogin', {
headers: {
cookie: req.headers.cookie,
},
});
return response.data;
if (decodedClaims && decodedClaims.donor) {
let doc = await admin.firestore().collection('donors').doc(uid).get();
return doc.data();
}

if (decodedClaims && decodedClaims.npo) {
let doc = await admin.firestore().collection('npos').doc(uid).get();
return doc.data();
}

let doc = await admin.firestore().collection('donors').doc(uid).get();
if (doc.exists) {
return doc.data();
} else {
let doc = await admin.firestore().collection('npos').doc(uid).get();
return doc.data();
}
} catch (error) {
res.writeHead(302, { Location: '/login' });
res.end();
return null;
if (error instanceof AuthError) {
throw new AuthError('user-does-not-exist', 'User does not exists');
} else {
throw new Error('Unknown error in getUser');
}
}
}
13 changes: 13 additions & 0 deletions utils/firebase/deserializerNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import admin from '@utils/admin-firebase';

export const deserializeFirestoreTimestampToUnixTimestampNode = (...objects) => {
for (let object of objects) {
for (let [key, value] of Object.entries(object)) {
if (value instanceof admin.firestore.Timestamp) {
object[key] = value.toMillis();
} else if (value instanceof Object) {
deserializeFirestoreTimestampToUnixTimestampNode(value);
}
}
}
};