Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verification Server integration with cookie/session #307

Merged
merged 11 commits into from Jul 8, 2020
11 changes: 6 additions & 5 deletions .github/workflows/build-and-test.yml
Expand Up @@ -39,14 +39,15 @@ jobs:
run: npm install
- name: Initialize config files
working-directory: cloud-functions/functions
run: npm run init
run: npm run init
- name: Setup functions config
working-directory: cloud-functions/functions
run: npm run config:set
env:
env:
FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
SENDGRID_API_TOKEN: ${{ secrets.SENDGRID_API_TOKEN }}
SENDGRID_API_KEY: ${{ secrets.SENDGRID_API_KEY }}
NODE_ENV: test
VERIF_SERVER_PASSWORD: ${{ secrets.VERIF_SERVER_PASSWORD }}
- name: Lint and Build
working-directory: cloud-functions/functions
run: |
Expand All @@ -71,7 +72,7 @@ jobs:
with:
name: cloud-functions-artifact
path: cloud-functions/functions
- name: Install
- name: Install
working-directory: cloud-functions/functions
run: npm install
- name: Deploy to Firebase Test Infra
Expand All @@ -92,7 +93,7 @@ jobs:
with:
name: cloud-functions-artifact
path: cloud-functions/functions
- name: Install
- name: Install
working-directory: cloud-functions/functions
run: npm install
- name: Run Tests
Expand Down
14 changes: 8 additions & 6 deletions .github/workflows/deploy.yml
Expand Up @@ -65,23 +65,25 @@ jobs:
run: npm install
- name: Initialize config files
working-directory: cloud-functions/functions
run: npm run init
run: npm run init
- name: Setup functions config
if: github.ref == 'refs/heads/dev'
working-directory: cloud-functions/functions
run: npm run config:set
env:
env:
FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
SENDGRID_API_TOKEN: ${{ secrets.SENDGRID_API_TOKEN }}
SENDGRID_API_KEY: ${{ secrets.SENDGRID_API_KEY }}
NODE_ENV: staging
VERIF_SERVER_PASSWORD: ${{ secrets.VERIF_SERVER_PASSWORD }}
- name: Setup functions config
if: github.ref == 'refs/heads/master'
working-directory: cloud-functions/functions
run: npm run config:set
env:
env:
FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
SENDGRID_API_TOKEN: ${{ secrets.SENDGRID_API_TOKEN }}
SENDGRID_API_KEY: ${{ secrets.SENDGRID_API_KEY }}
NODE_ENV: prod
VERIF_SERVER_PASSWORD: ${{ secrets.VERIF_SERVER_PASSWORD }}
- name: Lint and Build
working-directory: cloud-functions/functions
run: |
Expand All @@ -108,7 +110,7 @@ jobs:
name: cloud-functions-artifact
path: cloud-functions/functions

- name: Install
- name: Install
working-directory: cloud-functions/functions
run: npm install

Expand Down
12 changes: 8 additions & 4 deletions cloud-functions/README.md
Expand Up @@ -15,13 +15,17 @@ In depth discussion of app and data model can be found in [Notion](https://www.n

## Setup your own local environment
2. `npm run init` to initialize common firebase files
3. Local mode runs in your personal firebase account. Create your own firebase project
a. create your personal firebase account ,
b. update your firebase configuration ( Firebase Console => Project => Settings => Website Settings ) in
$ROOT/cloud-functions/functions/config/firebase.config.dev.js
3. Local mode runs in your personal firebase account. Create your own firebase project and add a web app to it (Firebase Console => Project => Project Settings => Your Apps => Add app)
a. create your personal firebase account
b. update your firebase configuration ( Firebase Console => Project => Project Settings => Your Apps => Firebase Sdk snippet => config ) in config/firebase.config.local.js
c. Update the `local` field in cloud-functions/.firebaserc to the name of your project
d. Run `firebase use local` from the cloud-functions directory
e. Deploy indexes with `firebase deploy --only firestore:indexes --project local` from the cloud-functions directory
3. a. create a firebase private key under (Firebase Console => Project => Settings => Service Account )
b. save the json under $ROOT/cloud-functions/functions/permission-portal-local-firebase-admin-key.json.
NOTE: It is important to have this specific file name because this is how the environment is tied to the firebase secret.
4. Enable email and password sign in (Firebase Console => Project => Sign-in method => Email/Password)
5.

4. In all the below steps , your local environment can be used with environment parameter="local"

Expand Down
106 changes: 93 additions & 13 deletions cloud-functions/functions/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions cloud-functions/functions/package.json
Expand Up @@ -21,14 +21,20 @@
},
"main": "lib/src/index.js",
"dependencies": {
"@sendgrid/mail": "^7.1.0",
"@types/tough-cookie": "^4.0.0",
"axios": "^0.19.2",
"axios-cookiejar-support": "^1.0.0",
"cpy-cli": "^3.1.1",
"dotenv": "^8.2.0",
"@sendgrid/mail": "^7.1.0",
"firebase": "^7.14.2",
"firebase-admin": "^8.10.0",
"firebase-functions": "^3.6.1",
"jest-environment-uint8array": "^1.0.0",
"reflect-metadata": "^0.1.13"
"query-string": "^6.13.1",
"querystring": "^0.2.0",
"reflect-metadata": "^0.1.13",
"tough-cookie": "^4.0.0"
},
"devDependencies": {
"@types/jest": "^24.9.1",
Expand Down
47 changes: 47 additions & 0 deletions cloud-functions/functions/src/index.ts
Expand Up @@ -2,6 +2,10 @@ import * as admin from 'firebase-admin';
import * as functions from 'firebase-functions';
import * as sgMail from '@sendgrid/mail';
import { randomBytes } from 'crypto';
import axios from 'axios';
import axiosCookieJarSupport from 'axios-cookiejar-support';
import * as toughCookie from 'tough-cookie';
import * as queryString from 'query-string';

// Initialize firebse admin and get db instance
admin.initializeApp(functions.config().firebase);
Expand Down Expand Up @@ -440,3 +444,46 @@ export const initiatePasswordRecovery = functions.https.onCall((body) => {
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please apply eslint (frontend/.eslintrc.js) and prettier (frontend/.prettierrc) to this file, there are a bunch of formatting discrepancies, several let's that should be const's, etc.

});
});

export const getVerificationCode = functions.https.onCall(async () => {
axiosCookieJarSupport(axios);

const config = functions.config().verif_server;
const url = config.url.slice(-1) === '/' ? config.url : config.url + '/';

const cookieJar = new toughCookie.CookieJar();
const instance = await axios.create({
jar: cookieJar,
withCredentials: true,
});

try {
let response = await instance.post(
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=' + config.key,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, where did you find this url? Do others do this non-firebase-client firebase auth login elsewhere (I imagine so)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got this from logging in to the Google Verif Server UI and looking at the network requests it made in the browser dev tools.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW here is the official documentation for this API endpoint

{ email: config.email, password: config.password, returnSecureToken: true }
);

const form = { idToken: response.data.idToken };

// Get CSRF token
response = await instance.get(url);

response = await instance.post(url + 'session', queryString.stringify(form), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-CSRF-Token': response.headers['x-csrf-token'],
},
});

response = await instance.get(url + 'home/csrf');
response = await instance.post(
url + 'home/issue',
{ testType: 'confirmed' },
{ headers: { 'X-CSRF-TOKEN': response.data.csrftoken } }
);
return response.data.code;
} catch (err) {
console.error(err);
throw err;
}
});