├── .gitignore
├── .node-version
├── articles
└── firebase-in-2019.md
├── cloud-firestore
├── README.md
├── bin
│ └── upload-data.js
├── complete.html
├── data
│ └── people.json
├── index.html
├── package.json
└── yarn.lock
├── firebase-authentication
├── 01-intro.md
├── 02-configuration.md
├── 03-implementation.md
├── 04-outro.md
├── 05-notes.md
└── screenshots
│ ├── 1000-auth-methods.png
│ ├── 1100-sign-in-method-enabled.png
│ ├── 1200-auth-domain-enabled.png
│ ├── 2000-facebook-read-docs.png
│ ├── 2050-facebook-create-app.png
│ ├── 2100-facebook-add-login.png
│ ├── 2200-facebook-copy-app-id-and-secret.png
│ ├── 2300-facebook-paste-id-and-secret.png
│ ├── 2400-facebook-paste-redirect-url.png
│ ├── 3000-go-do-dashboard.png
│ ├── 3100-copy-config.png
│ ├── 3300-auth-domain-initialized.png
│ ├── 3400-init-js.png
│ ├── 3500-folder-structure.png
│ ├── 4000-auth-service.png
│ ├── 4100-auth-service-return.png
│ ├── 4200-method-signature.png
│ ├── 4300-onAuthStateChanged.png
│ ├── 4350-sign-out-delete.png
│ ├── 4400-email-password.png
│ ├── 4500-password-reset.png
│ ├── 4600-phone.png
│ ├── 4700-recaptcha-verifier.png
│ ├── 4800-oauth-sign-in.png
│ ├── 5000-login-options.png
│ ├── 5100-input-email.png
│ ├── 5200-input-password.png
│ ├── 5300-logged-in.png
│ ├── 5400-bad-password.png
│ ├── 5500-input-email-two.png
│ ├── 5600-input-password-two.png
│ ├── 5700-register-email.png
│ ├── 5800-input-phone.png
│ ├── 5900-confirm-phone.png
│ └── 5950-oauth-google.png
├── firebase-cloud-functions
├── .firebaserc
├── README.md
├── context.json
├── firebase.json
├── functions
│ ├── config.json.dist
│ ├── index.js
│ ├── index.spec.js
│ ├── package.json
│ └── yarn.lock
├── snap.val.json
└── spec
│ └── support
│ └── jasmine.json
├── starter-project
├── .babelrc
├── .firebaserc
├── database.rules.json
├── firebase.json
├── firestore.indexes.json
├── firestore.rules
├── functions
│ ├── index.js
│ ├── package.json
│ └── yarn.lock
├── index.html
├── package-lock.json
├── package.json
├── public
│ └── index.html
├── src
│ ├── index.html
│ └── index.js
├── storage.rules
└── yarn.lock
└── write-your-first-query
├── README.md
├── bin
└── upload-data.js
├── complete.html
├── data
└── people.json
├── index.html
├── package.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 |
39 | config.json
40 | *.log
41 |
42 | .vscode
43 |
44 | dist
45 | .cache
46 |
47 | service-account.json
--------------------------------------------------------------------------------
/.node-version:
--------------------------------------------------------------------------------
1 | lts
2 |
--------------------------------------------------------------------------------
/articles/firebase-in-2019.md:
--------------------------------------------------------------------------------
1 | # Firebase: 2019's Dominant Web App Platform
2 |
3 | ## tldr;
4 |
5 | Google has invested heavily to make Firebase a full-featured application platform for web. It's dirt cheap to use. It's reliable. It saves up to 50% of your dev time.
6 |
7 | Firebase is all you need... with a few exceptions. Firebase is still missing search functionality, but we can plug that hole with Algolia. Google Cloud Platform provides a few more ancillary services such as DNS, Cloud SQL for reporting data and Bigtable for graph data.
8 |
9 | ## A Quick History
10 |
11 | Firebase was [birthed from a Y Combinator company](https://hackernoon.com/how-to-build-a-product-loved-by-millions-and-get-acquired-by-google-the-firebase-story-82dab4e3e80c) that was acquired by Google in 2014.
12 |
13 | Firebase is a classic [disruptive company](http://claytonchristensen.com/key-concepts/). It started as a quick prototyping tool for developers who wanted to move quickly but didn't necessarily need much scale or sophistication.
14 |
15 | They've spent the last eight years building capabilities from that original base of passionate freelancers and indie devs, and as a cornerstone of Google Cloud Platform's strategy, Firebase is fighting its way into the larger corporate market.
16 |
17 | ## What is Firebase?
18 |
19 | Firebase is a suite of managed services that are focused on developer experience over raw power.
20 |
21 | Firebase handles your authentication, data storage, document storage and serverless functions.
22 |
23 | AWS and GCP appeal to large, corporate projects that can take years to ship.
24 |
25 | Use Firebase when you need to ship yesterday.
26 |
27 | You may have heard that Firebase is just for startups or prototypes, or that it fails at scale or gets super expensive. Well... things have changed. A lot.
28 |
29 | ## Firebase teaches front-end devs to use a distributed architecture
30 |
31 | Firebase enables a distributed architecture.
32 |
33 | New developers have traditionally learned to build monolithic apps.
34 |
35 | A monolith is a single application server that holds your database and server. Everything lives on a single server that you control, so it's very easy to teach and understand.
36 |
37 | The first managed service that I used was Amazon S3. It was an endpoint where I could send files. S3 would save the files and give me a link. I'd save the link in my local SQL database and S3 would serve the file to my users.
38 |
39 | Managed services are used to create distributed systems. Each part of the system is optimized for a small part of the puzzle. Developers compose these systems together.
40 |
41 | AWS and GCP provide extremely powerful, complicated managed services aimed at large, enterprise users. They're not built for indie devs. And that's where Firebase comes in.
42 |
43 | Firebase lets you quickly deploy managed services for small- to medium-sized apps. And you can roll in heavier-duty Google Cloud Platform (GCP) services as your app grows.
44 |
45 | ## An alternative to SQL
46 |
47 | Most developers are raised on SQL.
48 |
49 | SQL is great for modeling relational data, but guess what! Most data is not as relational as you may think.
50 |
51 | SQL is ideal when you don't know how you're going to query your data. You simply store your data in [third normal form](https://en.wikipedia.org/wiki/Third_normal_form) and SQL lets you query it into almost any structure for your front end.
52 |
53 | Firebase and other NoSQL datastores optimize for reads, which are much more common than writes. So instead of writing once and reading in many different ways like with SQL... NoSQL/Firebase asks you to write the data to match your reads.
54 |
55 | ## Firebase crushes other databases (for many use cases)
56 |
57 | Let's say that I have new user sign up for a multi-player RPG. I want to show that user's email address on their profile, next to any messages that they send to other users. Finally, I'll need a list of all user emails for my admin dashboard.
58 |
59 | Firebase asks you to write that email address in three different places, one for each type of read operation. "But Chris!" you ask. What if I need to change the email address??? Isn't that inefficient???
60 |
61 | Yep. It's inefficient. You'll have to update that email address in three places, and you'll have to be careful to track everywhere that you saved it.
62 |
63 | But how often do you actually need to **write** an email address? What about the reads???
64 |
65 | The reality, and the reason that Firebase works so well, is that most apps **read data significantly more** than they write it.
66 |
67 | So get over your SQL-induced anxiety and duplicate data throughout your Firebase data structure. Your database reads will dramatically outperform SQL reads at scale.
68 |
69 | ## The Evil Twin Databases
70 |
71 | Firebase now has two databases,
72 |
73 | 1. the [Realtime Database](https://firebase.google.com/docs/database/), also known as classic Firebase or the RTDB, and
74 | 2. [Cloud Firestore](https://firebase.google.com/docs/firestore/).
75 |
76 | The Realtime Database is ideal for fast, high-volume operations. I use the RTDB for tracking how many users I have online at any one time. I use it for job queues that are consumed with [Cloud Functions](https://firebase.google.com/docs/functions/).
77 |
78 | Firestore is better for longer-lived, more structured data. I use Firestore for 90% of my data storage. You pay by the operation, so it's not ideal for extremely frequent reads and writes... but that's why we have the RTDB :)
79 |
80 | I use Firestore for user profiles, chat logs, purchase records... anything that I need to stick around for a while.
81 |
82 | ## Firebase is cheap and much, much faster
83 |
84 | The [Iron Triangle of Project Management](https://en.wikipedia.org/wiki/Project_management_triangle) states that all projects are compromises between **cost**, **scope** and **time**.
85 |
86 | Firebase is the lowest-cost and fastest development experience of which I'm aware.
87 |
88 | I bootstrap projects on a regular basis. Most of my projects are solo projects. I'm the developer, designer and marketer. I'm incredibly time-constrained, so I use Firebase to enable me to achieve greater scope while keeping my time costs low.
89 |
90 | I've built the following projects entirely on my own with Firebase:
91 |
92 | - [calligraphy.org](https://www.calligraphy.org/): Online teaching platform integrated with Shopify
93 | - [HiiTClock.com](https://www.hiitclock.com/): PWA workout timer
94 | - [pixels.chrisesplin.com](https://pixels.chrisesplin.com/): Chrome extension for UI development
95 | - [bunches.chrisesplin.com](https://bunches.chrisesplin.com/): Multi-player card game
96 |
97 | Calligraphy.org powers my wife's business. It took me four months to write it in the mornings before my full-time job. It would have taken twice as long without Firebase.
98 |
99 | The other projects took between 75 and 200 hours each. Again, I finished them in my spare time while relying heavily on Firebase.
100 |
101 | Firebase has spoiled me. I've used Firebase instead of SQL since 2013. And these aren't just rapid prototypes. They're fully-functional, scalable and used by thousands of customers.
102 |
103 | ## Firebase includes _nearly_ everything I need to build small- to medium-sized apps
104 |
105 | The pillars of Firebase's web offering are
106 |
107 | - the Realtime Database (json database),
108 | - Cloud Firestore (document/collection database),
109 | - Cloud Functions for Firebase (serverless functions),
110 | - Firebase Storage (file/blob storage), and
111 | - Firebase Hosting (static file hosting).
112 |
113 | These five pillars can support an enormous range of apps.
114 |
115 | Realistically, you'll need to "cheat" on Firebase a bit for larger apps. I use GCP for a few things that don't fit neatly within the Firebase offering. I also use [Algolia](https://www.algolia.com/) to power my search.
116 |
117 | The funny thing is that Algolia is the most expensive part of my stack at $35/month. The Firebase databases are optimized for everything **except search**. This is an important caveat to recognize early. Searching Firestore collections or anything in the Realtime Database is extremely limited, and for strong architectural reasons. I don't expect to ever see Firebase support search.
118 |
119 | I primarily use GCP to manage the DNS for my domains. And I sometimes run Cloud Compute instances for small, custom tasks.
120 |
121 | I also use [GitLab.com](https://gitlab.com/deltaepsilon) for CI/CD purposes... so I guess I step out on Firebase a couple of times on each project :)
122 |
123 | ## Firebase scales effortlessly
124 |
125 | Firebase does not allow slow operations. The database does not execute joins. It doesn't search.
126 |
127 | I haven't personally built a Firebase app up to massive scale. I've heard that the Realtime Database can run into limits and require manual sharding; however, Firestore is architected much differently, and I wouldn't be surprised if it scales more like [Cloud Spanner](https://cloud.google.com/spanner/).
128 |
129 | Firestore has some awesome performance characteristics.
130 |
131 | From the [Firestore marketing page](https://firebase.google.com/products/firestore/):
132 |
133 | > All queries scale with the size of your result set (note: not your data set), so your app is ready to scale from day one.
134 |
135 | Firestore also offers strong consistency, meaning that you don't have to worry about the eventual-consistency data models that bedevil most NoSQL implementations.
136 |
137 | ## GCP is the escape hatch
138 |
139 | Firebase services are built on Google Cloud Platform (aka GCP) infrastructure, and some of them are closely integrated.
140 |
141 | For instance, Firebase Cloud Storage uses GCP Storage buckets that you can access through the GCP SDKs.
142 |
143 | GCP is an enterprise-focused suite of services. It's massive.
144 |
145 | Each Firebase project comes with its own GCP account, granting you access to the full power of GCP. So don't worry if Firebase doesn't fulfill your every need. GCP has it covered.
146 |
147 | For example, If you don't want to pay for Algolia, you can spin up an Elasticsearch cluster on GCP and roll your own search. It'll be just as expensive as Algolia... so I can't recommend it for small projects; however, those capabilities are all there.
148 |
149 | ## Common concerns with Firebase
150 |
151 | Firebase has it's downsides!
152 |
153 | - Firebase cannot be hosted locally.
154 | - Firebase is tough to mock for local unit tests.
155 | - There's far less tooling available than for SQL databases.
156 | - Firebase can get expensive if you implement it poorly.
157 |
158 | None of these problems bothers me. It may be Stockholm Syndrome... but I'm at peace with all of these "problems" with Firebase.
159 |
160 | Engineering is all about tradeoffs. Firebase trades a bunch of control and autonomy for speed and simplicity of development.
161 |
162 | ## When to NOT use Firebase
163 |
164 | **DO NOT** use Firebase for highly relational data.
165 |
166 | **DO NOT** use Firebase for complex graph data.
167 |
168 | **DO NOT** use Firebase for complex server needs.
169 |
170 | I sometimes recommend hybrid architectures. Store your relational data in GCP's Cloud SQL or Cloud Spanner databases. Store your graph data in JanusGraph on top of Cloud Bigtable.
171 |
172 | But these are all advanced use cases! Don't worry about them until you need them.
173 |
174 | ## The competition
175 |
176 | Firebase's direct competition is dead. [Facebook killed off Parse](https://blog.parseplatform.org/announcements/a-parse-shutdown-reminder/) and [RethinkDB](https://rethinkdb.com/blog/rethinkdb-shutdown/) suffered a similar fate.
177 |
178 | The most direct alternative to Firebase at this point is AWS or GCP. They're much more complicated and harder to use... but you could achieve similar architectures with other services.
179 |
180 | Or just use Postgres. Postgres is killer... as long as you have budget to write your own API on top of it.
181 |
182 | That's about it. Use AWS, GCP or Postgres. Your dev velocity will suffer, but at least with Postgres you can run everything locally!
183 |
184 | I've worked in a corporate setting with Postgres running in a local Docker container, and it's slick. I didn't write that API myself. It was expensive to maintain, and I could have duplicated it in Firebase at a much lower cost. But the business I was working with wasn't a fan of managed services. What could I do :)
185 |
186 | ## Conclusion: Firebase is preferred for most front-end-focused apps
187 |
188 | Firebase is a slam dunk for small- to medium-sized projects, especially if they're front-end focused.
189 |
190 | You may need to re-architect if you hit massive scale... but you **always** have to re-architect for massive scale. Don't prematurely optimize for scale.
191 |
192 | Choose Firebase because it gets you to market faster. It helps you validate your ideas and get feedback.
193 |
194 | Firebase is a cornerstone of Google's cloud strategy. It's not going anywhere.
195 |
--------------------------------------------------------------------------------
/cloud-firestore/README.md:
--------------------------------------------------------------------------------
1 | # Write Your First Query
2 |
3 | ## Open in Glitch
4 |
5 | We'll be working in Google Chrome.
6 |
7 | Open the Glitch project with Google Chrome: [Write Your First Query](https://glitch.com/edit/#!/coordinated-freighter?path=index.html)
8 |
9 | ## DevTools
10 |
11 | Click the green Show (Live) button in the header of the Glitch project to open a new window.
12 |
13 | The new window has a live view of your project, so you can see what happens as you edit it.
14 |
15 | Right-click on the page and select "Inspect Element".
16 |
17 | The new panel that has opened up is called Dev Tools.
18 |
19 | Click the "three dots" button on the top-right of Dev Tools and select Dock Side > Dock to Right.
20 |
21 | Now expand drag DevTools to be nice and big and select the Console tab.
22 |
23 |
24 | ## Write the code
25 |
26 | Return to Glitch and start writing.
27 |
28 | Switch to the live-preview tab to see your progress!
29 |
30 | ## Files
31 |
32 | The files are in this repo.
33 |
34 | They're also available at the following links:
35 |
36 | - [index.html](https://raw.githubusercontent.com/how-to-firebase/tutorials/master/write-your-first-query/index.html)
37 | - [complete.html](https://raw.githubusercontent.com/how-to-firebase/tutorials/master/write-your-first-query/complete.html)
38 |
39 |
--------------------------------------------------------------------------------
/cloud-firestore/bin/upload-data.js:
--------------------------------------------------------------------------------
1 | const admin = require('firebase-admin');
2 | const serviceAccount = require('../../service-account.json');
3 |
4 | admin.initializeApp({
5 | credential: admin.credential.cert(serviceAccount),
6 | databaseURL: 'https://how-to-firebase-tutorials.firebaseio.com',
7 | });
8 |
9 | const people = require('../data/people.json');
10 |
11 | const records = people.map(({ name, height, hair_color, url, mass, eye_color, gender }) => {
12 | const urlParts = url.split('/');
13 | let id = String(urlParts[urlParts.length - 2]);
14 | let charactersToPad = 3 - id.length;
15 | while (charactersToPad--) {
16 | id = '0' + id;
17 | }
18 |
19 | return {
20 | id,
21 | name,
22 | height: +height,
23 | hair_color,
24 | mass: +mass,
25 | eye_color,
26 | gender,
27 | };
28 | });
29 |
30 | const db = admin.firestore();
31 | const collection = db
32 | .collection('public')
33 | .doc('cloud-firestore')
34 | .collection('star-wars-people');
35 | const batch = db.batch();
36 |
37 | records.forEach(({ id, name, height, hair_color, mass, eye_color, gender }) => {
38 | batch.set(collection.doc(id), { name, height, hair_color, mass, eye_color, gender });
39 | });
40 |
41 | batch.commit().then(() => {
42 | console.log('people records saved');
43 | });
44 |
--------------------------------------------------------------------------------
/cloud-firestore/complete.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/cloud-firestore/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "write-your-first-query",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "dependencies": {
7 | "firebase-admin": "^5.8.2"
8 | },
9 | "scripts": {
10 | "upload": "node bin/upload-data.js"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/cloud-firestore/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@firebase/app-types@0.1.1":
6 | version "0.1.1"
7 | resolved "https://registry.yarnpkg.com/@firebase/app-types/-/app-types-0.1.1.tgz#1b794e101c779310763b1bfce8c24e7728fb9a91"
8 |
9 | "@firebase/app@^0.1.1":
10 | version "0.1.8"
11 | resolved "https://registry.yarnpkg.com/@firebase/app/-/app-0.1.8.tgz#0952d0c0cb2926aaa9b39459c7d9df653fa54330"
12 | dependencies:
13 | "@firebase/app-types" "0.1.1"
14 | "@firebase/util" "0.1.8"
15 |
16 | "@firebase/database-types@0.1.1":
17 | version "0.1.1"
18 | resolved "https://registry.yarnpkg.com/@firebase/database-types/-/database-types-0.1.1.tgz#601b8040191766777b785c1675eac34ce57c669c"
19 |
20 | "@firebase/database@^0.1.3":
21 | version "0.1.9"
22 | resolved "https://registry.yarnpkg.com/@firebase/database/-/database-0.1.9.tgz#ea5f376d1a59d16f909dfb46c597b55e35d9834b"
23 | dependencies:
24 | "@firebase/database-types" "0.1.1"
25 | "@firebase/util" "0.1.8"
26 | faye-websocket "0.11.1"
27 |
28 | "@firebase/util@0.1.8":
29 | version "0.1.8"
30 | resolved "https://registry.yarnpkg.com/@firebase/util/-/util-0.1.8.tgz#7a7eb9d5fc56ba9e9b854bb2357d51f83b07df31"
31 |
32 | "@google-cloud/common-grpc@^0.5.3":
33 | version "0.5.4"
34 | resolved "https://registry.yarnpkg.com/@google-cloud/common-grpc/-/common-grpc-0.5.4.tgz#bbc36de0c01cf101dcb2660d76c8b833db6f7a23"
35 | dependencies:
36 | "@google-cloud/common" "^0.15.1"
37 | dot-prop "^4.2.0"
38 | duplexify "^3.5.1"
39 | extend "^3.0.1"
40 | grpc "~1.7.2"
41 | is "^3.2.0"
42 | modelo "^4.2.0"
43 | retry-request "^3.3.1"
44 | through2 "^2.0.3"
45 |
46 | "@google-cloud/common@^0.15.1":
47 | version "0.15.1"
48 | resolved "https://registry.yarnpkg.com/@google-cloud/common/-/common-0.15.1.tgz#a6f79535de626cc0e05b0ccda9db0c36099e47a3"
49 | dependencies:
50 | array-uniq "^1.0.3"
51 | arrify "^1.0.1"
52 | concat-stream "^1.6.0"
53 | create-error-class "^3.0.2"
54 | duplexify "^3.5.0"
55 | ent "^2.2.0"
56 | extend "^3.0.1"
57 | google-auto-auth "^0.8.0"
58 | is "^3.2.0"
59 | log-driver "^1.2.5"
60 | methmeth "^1.1.0"
61 | modelo "^4.2.0"
62 | request "^2.79.0"
63 | retry-request "^3.0.0"
64 | split-array-stream "^1.0.0"
65 | stream-events "^1.0.1"
66 | string-format-obj "^1.1.0"
67 | through2 "^2.0.3"
68 |
69 | "@google-cloud/firestore@^0.11.2":
70 | version "0.11.2"
71 | resolved "https://registry.yarnpkg.com/@google-cloud/firestore/-/firestore-0.11.2.tgz#b511a1ec7a3b9df5cc453034b24391b0ccff9b1d"
72 | dependencies:
73 | "@google-cloud/common" "^0.15.1"
74 | "@google-cloud/common-grpc" "^0.5.3"
75 | bun "^0.0.12"
76 | extend "^3.0.1"
77 | functional-red-black-tree "^1.0.1"
78 | google-gax "^0.14.3"
79 | is "^3.2.1"
80 | safe-buffer "^5.1.1"
81 | through2 "^2.0.3"
82 |
83 | "@google-cloud/storage@^1.2.1":
84 | version "1.5.2"
85 | resolved "https://registry.yarnpkg.com/@google-cloud/storage/-/storage-1.5.2.tgz#5e206603570f5d1cee4681b23919d8f3655e4e60"
86 | dependencies:
87 | "@google-cloud/common" "^0.15.1"
88 | arrify "^1.0.0"
89 | async "^2.0.1"
90 | concat-stream "^1.5.0"
91 | create-error-class "^3.0.2"
92 | duplexify "^3.5.0"
93 | extend "^3.0.0"
94 | gcs-resumable-upload "^0.8.2"
95 | hash-stream-validation "^0.2.1"
96 | is "^3.0.1"
97 | mime-types "^2.0.8"
98 | once "^1.3.1"
99 | pumpify "^1.3.3"
100 | request "^2.83.0"
101 | safe-buffer "^5.1.1"
102 | snakeize "^0.1.0"
103 | stream-events "^1.0.1"
104 | string-format-obj "^1.0.0"
105 | through2 "^2.0.0"
106 |
107 | "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
108 | version "1.1.2"
109 | resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"
110 |
111 | "@protobufjs/base64@^1.1.2":
112 | version "1.1.2"
113 | resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735"
114 |
115 | "@protobufjs/codegen@^2.0.4":
116 | version "2.0.4"
117 | resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb"
118 |
119 | "@protobufjs/eventemitter@^1.1.0":
120 | version "1.1.0"
121 | resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70"
122 |
123 | "@protobufjs/fetch@^1.1.0":
124 | version "1.1.0"
125 | resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45"
126 | dependencies:
127 | "@protobufjs/aspromise" "^1.1.1"
128 | "@protobufjs/inquire" "^1.1.0"
129 |
130 | "@protobufjs/float@^1.0.2":
131 | version "1.0.2"
132 | resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1"
133 |
134 | "@protobufjs/inquire@^1.1.0":
135 | version "1.1.0"
136 | resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089"
137 |
138 | "@protobufjs/path@^1.1.2":
139 | version "1.1.2"
140 | resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d"
141 |
142 | "@protobufjs/pool@^1.1.0":
143 | version "1.1.0"
144 | resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54"
145 |
146 | "@protobufjs/utf8@^1.1.0":
147 | version "1.1.0"
148 | resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
149 |
150 | "@types/google-cloud__storage@^1.1.1":
151 | version "1.1.7"
152 | resolved "https://registry.yarnpkg.com/@types/google-cloud__storage/-/google-cloud__storage-1.1.7.tgz#f4b568b163cce16314f32f954f5b7d5c9001fa86"
153 | dependencies:
154 | "@types/node" "*"
155 |
156 | "@types/long@^3.0.32":
157 | version "3.0.32"
158 | resolved "https://registry.yarnpkg.com/@types/long/-/long-3.0.32.tgz#f4e5af31e9e9b196d8e5fca8a5e2e20aa3d60b69"
159 |
160 | "@types/node@*":
161 | version "9.4.2"
162 | resolved "https://registry.yarnpkg.com/@types/node/-/node-9.4.2.tgz#b109a6c4f64147ccf9476d9e1a6fbf69a10faeb8"
163 |
164 | "@types/node@^8.0.53", "@types/node@^8.5.5":
165 | version "8.9.0"
166 | resolved "https://registry.yarnpkg.com/@types/node/-/node-8.9.0.tgz#99449266e9f023cc3ad5de304d759de787d18ea4"
167 |
168 | abbrev@1:
169 | version "1.1.1"
170 | resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
171 |
172 | acorn-es7-plugin@^1.0.12:
173 | version "1.1.7"
174 | resolved "https://registry.yarnpkg.com/acorn-es7-plugin/-/acorn-es7-plugin-1.1.7.tgz#f2ee1f3228a90eead1245f9ab1922eb2e71d336b"
175 |
176 | acorn@^4.0.0:
177 | version "4.0.13"
178 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
179 |
180 | ajv@^4.9.1:
181 | version "4.11.8"
182 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536"
183 | dependencies:
184 | co "^4.6.0"
185 | json-stable-stringify "^1.0.1"
186 |
187 | ajv@^5.1.0:
188 | version "5.5.2"
189 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
190 | dependencies:
191 | co "^4.6.0"
192 | fast-deep-equal "^1.0.0"
193 | fast-json-stable-stringify "^2.0.0"
194 | json-schema-traverse "^0.3.0"
195 |
196 | ansi-regex@^2.0.0:
197 | version "2.1.1"
198 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
199 |
200 | aproba@^1.0.3:
201 | version "1.2.0"
202 | resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
203 |
204 | are-we-there-yet@~1.1.2:
205 | version "1.1.4"
206 | resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d"
207 | dependencies:
208 | delegates "^1.0.0"
209 | readable-stream "^2.0.6"
210 |
211 | arguejs@^0.2.3:
212 | version "0.2.3"
213 | resolved "https://registry.yarnpkg.com/arguejs/-/arguejs-0.2.3.tgz#b6f939f5fe0e3cd1f3f93e2aa9262424bf312af7"
214 |
215 | array-filter@^1.0.0:
216 | version "1.0.0"
217 | resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83"
218 |
219 | array-union@^1.0.1:
220 | version "1.0.2"
221 | resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
222 | dependencies:
223 | array-uniq "^1.0.1"
224 |
225 | array-uniq@^1.0.1, array-uniq@^1.0.3:
226 | version "1.0.3"
227 | resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
228 |
229 | arrify@^1.0.0, arrify@^1.0.1:
230 | version "1.0.1"
231 | resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
232 |
233 | ascli@~1:
234 | version "1.0.1"
235 | resolved "https://registry.yarnpkg.com/ascli/-/ascli-1.0.1.tgz#bcfa5974a62f18e81cabaeb49732ab4a88f906bc"
236 | dependencies:
237 | colour "~0.7.1"
238 | optjs "~3.2.2"
239 |
240 | asn1@~0.2.3:
241 | version "0.2.3"
242 | resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86"
243 |
244 | assert-plus@1.0.0, assert-plus@^1.0.0:
245 | version "1.0.0"
246 | resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
247 |
248 | assert-plus@^0.2.0:
249 | version "0.2.0"
250 | resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234"
251 |
252 | async@^2.0.1, async@^2.3.0, async@^2.4.0:
253 | version "2.6.0"
254 | resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4"
255 | dependencies:
256 | lodash "^4.14.0"
257 |
258 | asynckit@^0.4.0:
259 | version "0.4.0"
260 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
261 |
262 | aws-sign2@~0.6.0:
263 | version "0.6.0"
264 | resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f"
265 |
266 | aws-sign2@~0.7.0:
267 | version "0.7.0"
268 | resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
269 |
270 | aws4@^1.2.1, aws4@^1.6.0:
271 | version "1.6.0"
272 | resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
273 |
274 | balanced-match@^1.0.0:
275 | version "1.0.0"
276 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
277 |
278 | base64url@2.0.0, base64url@^2.0.0:
279 | version "2.0.0"
280 | resolved "https://registry.yarnpkg.com/base64url/-/base64url-2.0.0.tgz#eac16e03ea1438eff9423d69baa36262ed1f70bb"
281 |
282 | bcrypt-pbkdf@^1.0.0:
283 | version "1.0.1"
284 | resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d"
285 | dependencies:
286 | tweetnacl "^0.14.3"
287 |
288 | block-stream@*:
289 | version "0.0.9"
290 | resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a"
291 | dependencies:
292 | inherits "~2.0.0"
293 |
294 | boom@2.x.x:
295 | version "2.10.1"
296 | resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f"
297 | dependencies:
298 | hoek "2.x.x"
299 |
300 | boom@4.x.x:
301 | version "4.3.1"
302 | resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31"
303 | dependencies:
304 | hoek "4.x.x"
305 |
306 | boom@5.x.x:
307 | version "5.2.0"
308 | resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02"
309 | dependencies:
310 | hoek "4.x.x"
311 |
312 | brace-expansion@^1.1.7:
313 | version "1.1.8"
314 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292"
315 | dependencies:
316 | balanced-match "^1.0.0"
317 | concat-map "0.0.1"
318 |
319 | buffer-equal-constant-time@1.0.1:
320 | version "1.0.1"
321 | resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
322 |
323 | buffer-equal@^1.0.0:
324 | version "1.0.0"
325 | resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe"
326 |
327 | bun@^0.0.12:
328 | version "0.0.12"
329 | resolved "https://registry.yarnpkg.com/bun/-/bun-0.0.12.tgz#d54fae69f895557f275423bc14b404030b20a5fc"
330 | dependencies:
331 | readable-stream "~1.0.32"
332 |
333 | bytebuffer@~5:
334 | version "5.0.1"
335 | resolved "https://registry.yarnpkg.com/bytebuffer/-/bytebuffer-5.0.1.tgz#582eea4b1a873b6d020a48d58df85f0bba6cfddd"
336 | dependencies:
337 | long "~3"
338 |
339 | call-signature@0.0.2:
340 | version "0.0.2"
341 | resolved "https://registry.yarnpkg.com/call-signature/-/call-signature-0.0.2.tgz#a84abc825a55ef4cb2b028bd74e205a65b9a4996"
342 |
343 | camelcase@^2.0.1:
344 | version "2.1.1"
345 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
346 |
347 | capture-stack-trace@^1.0.0:
348 | version "1.0.0"
349 | resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d"
350 |
351 | caseless@~0.12.0:
352 | version "0.12.0"
353 | resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
354 |
355 | cliui@^3.0.3:
356 | version "3.2.0"
357 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
358 | dependencies:
359 | string-width "^1.0.1"
360 | strip-ansi "^3.0.1"
361 | wrap-ansi "^2.0.0"
362 |
363 | co@^4.6.0:
364 | version "4.6.0"
365 | resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
366 |
367 | code-point-at@^1.0.0:
368 | version "1.1.0"
369 | resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
370 |
371 | colour@~0.7.1:
372 | version "0.7.1"
373 | resolved "https://registry.yarnpkg.com/colour/-/colour-0.7.1.tgz#9cb169917ec5d12c0736d3e8685746df1cadf778"
374 |
375 | combined-stream@^1.0.5, combined-stream@~1.0.5:
376 | version "1.0.5"
377 | resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009"
378 | dependencies:
379 | delayed-stream "~1.0.0"
380 |
381 | concat-map@0.0.1:
382 | version "0.0.1"
383 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
384 |
385 | concat-stream@^1.5.0, concat-stream@^1.6.0:
386 | version "1.6.0"
387 | resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7"
388 | dependencies:
389 | inherits "^2.0.3"
390 | readable-stream "^2.2.2"
391 | typedarray "^0.0.6"
392 |
393 | configstore@^3.0.0:
394 | version "3.1.1"
395 | resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.1.tgz#094ee662ab83fad9917678de114faaea8fcdca90"
396 | dependencies:
397 | dot-prop "^4.1.0"
398 | graceful-fs "^4.1.2"
399 | make-dir "^1.0.0"
400 | unique-string "^1.0.0"
401 | write-file-atomic "^2.0.0"
402 | xdg-basedir "^3.0.0"
403 |
404 | console-control-strings@^1.0.0, console-control-strings@~1.1.0:
405 | version "1.1.0"
406 | resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
407 |
408 | core-js@^2.0.0:
409 | version "2.5.3"
410 | resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e"
411 |
412 | core-util-is@1.0.2, core-util-is@~1.0.0:
413 | version "1.0.2"
414 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
415 |
416 | create-error-class@^3.0.2:
417 | version "3.0.2"
418 | resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6"
419 | dependencies:
420 | capture-stack-trace "^1.0.0"
421 |
422 | cryptiles@2.x.x:
423 | version "2.0.5"
424 | resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8"
425 | dependencies:
426 | boom "2.x.x"
427 |
428 | cryptiles@3.x.x:
429 | version "3.1.2"
430 | resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe"
431 | dependencies:
432 | boom "5.x.x"
433 |
434 | crypto-random-string@^1.0.0:
435 | version "1.0.0"
436 | resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
437 |
438 | dashdash@^1.12.0:
439 | version "1.14.1"
440 | resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
441 | dependencies:
442 | assert-plus "^1.0.0"
443 |
444 | debug@^2.2.0:
445 | version "2.6.9"
446 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
447 | dependencies:
448 | ms "2.0.0"
449 |
450 | decamelize@^1.1.1:
451 | version "1.2.0"
452 | resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
453 |
454 | deep-extend@~0.4.0:
455 | version "0.4.2"
456 | resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f"
457 |
458 | define-properties@^1.1.2:
459 | version "1.1.2"
460 | resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94"
461 | dependencies:
462 | foreach "^2.0.5"
463 | object-keys "^1.0.8"
464 |
465 | delayed-stream@~1.0.0:
466 | version "1.0.0"
467 | resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
468 |
469 | delegates@^1.0.0:
470 | version "1.0.0"
471 | resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
472 |
473 | detect-libc@^1.0.2:
474 | version "1.0.3"
475 | resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
476 |
477 | diff-match-patch@^1.0.0:
478 | version "1.0.0"
479 | resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.0.tgz#1cc3c83a490d67f95d91e39f6ad1f2e086b63048"
480 |
481 | dir-glob@^2.0.0:
482 | version "2.0.0"
483 | resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034"
484 | dependencies:
485 | arrify "^1.0.1"
486 | path-type "^3.0.0"
487 |
488 | dot-prop@^4.1.0, dot-prop@^4.2.0:
489 | version "4.2.0"
490 | resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57"
491 | dependencies:
492 | is-obj "^1.0.0"
493 |
494 | duplexify@^3.5.0, duplexify@^3.5.1, duplexify@^3.5.3:
495 | version "3.5.3"
496 | resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.3.tgz#8b5818800df92fd0125b27ab896491912858243e"
497 | dependencies:
498 | end-of-stream "^1.0.0"
499 | inherits "^2.0.1"
500 | readable-stream "^2.0.0"
501 | stream-shift "^1.0.0"
502 |
503 | eastasianwidth@^0.1.1:
504 | version "0.1.1"
505 | resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.1.1.tgz#44d656de9da415694467335365fb3147b8572b7c"
506 |
507 | ecc-jsbn@~0.1.1:
508 | version "0.1.1"
509 | resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505"
510 | dependencies:
511 | jsbn "~0.1.0"
512 |
513 | ecdsa-sig-formatter@1.0.9:
514 | version "1.0.9"
515 | resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz#4bc926274ec3b5abb5016e7e1d60921ac262b2a1"
516 | dependencies:
517 | base64url "^2.0.0"
518 | safe-buffer "^5.0.1"
519 |
520 | empower-core@^0.6.2:
521 | version "0.6.2"
522 | resolved "https://registry.yarnpkg.com/empower-core/-/empower-core-0.6.2.tgz#5adef566088e31fba80ba0a36df47d7094169144"
523 | dependencies:
524 | call-signature "0.0.2"
525 | core-js "^2.0.0"
526 |
527 | empower@^1.2.3:
528 | version "1.2.3"
529 | resolved "https://registry.yarnpkg.com/empower/-/empower-1.2.3.tgz#6f0da73447f4edd838fec5c60313a88ba5cb852b"
530 | dependencies:
531 | core-js "^2.0.0"
532 | empower-core "^0.6.2"
533 |
534 | end-of-stream@^1.0.0, end-of-stream@^1.1.0:
535 | version "1.4.1"
536 | resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
537 | dependencies:
538 | once "^1.4.0"
539 |
540 | ent@^2.2.0:
541 | version "2.2.0"
542 | resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d"
543 |
544 | espurify@^1.6.0:
545 | version "1.7.0"
546 | resolved "https://registry.yarnpkg.com/espurify/-/espurify-1.7.0.tgz#1c5cf6cbccc32e6f639380bd4f991fab9ba9d226"
547 | dependencies:
548 | core-js "^2.0.0"
549 |
550 | estraverse@^4.1.0, estraverse@^4.2.0:
551 | version "4.2.0"
552 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
553 |
554 | extend@^3.0.0, extend@^3.0.1, extend@~3.0.0, extend@~3.0.1:
555 | version "3.0.1"
556 | resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
557 |
558 | extsprintf@1.3.0:
559 | version "1.3.0"
560 | resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
561 |
562 | extsprintf@^1.2.0:
563 | version "1.4.0"
564 | resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
565 |
566 | fast-deep-equal@^1.0.0:
567 | version "1.0.0"
568 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff"
569 |
570 | fast-json-stable-stringify@^2.0.0:
571 | version "2.0.0"
572 | resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
573 |
574 | faye-websocket@0.11.1:
575 | version "0.11.1"
576 | resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.1.tgz#f0efe18c4f56e4f40afc7e06c719fd5ee6188f38"
577 | dependencies:
578 | websocket-driver ">=0.5.1"
579 |
580 | faye-websocket@0.9.3:
581 | version "0.9.3"
582 | resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.9.3.tgz#482a505b0df0ae626b969866d3bd740cdb962e83"
583 | dependencies:
584 | websocket-driver ">=0.5.1"
585 |
586 | firebase-admin@^5.8.2:
587 | version "5.8.2"
588 | resolved "https://registry.yarnpkg.com/firebase-admin/-/firebase-admin-5.8.2.tgz#4c390df3b6fd32567d8d4558ac04974bebd15749"
589 | dependencies:
590 | "@firebase/app" "^0.1.1"
591 | "@firebase/database" "^0.1.3"
592 | "@google-cloud/firestore" "^0.11.2"
593 | "@google-cloud/storage" "^1.2.1"
594 | "@types/google-cloud__storage" "^1.1.1"
595 | "@types/node" "^8.0.53"
596 | faye-websocket "0.9.3"
597 | jsonwebtoken "8.1.0"
598 | node-forge "0.7.1"
599 |
600 | foreach@^2.0.5:
601 | version "2.0.5"
602 | resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
603 |
604 | forever-agent@~0.6.1:
605 | version "0.6.1"
606 | resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
607 |
608 | form-data@~2.1.1:
609 | version "2.1.4"
610 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1"
611 | dependencies:
612 | asynckit "^0.4.0"
613 | combined-stream "^1.0.5"
614 | mime-types "^2.1.12"
615 |
616 | form-data@~2.3.1:
617 | version "2.3.1"
618 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf"
619 | dependencies:
620 | asynckit "^0.4.0"
621 | combined-stream "^1.0.5"
622 | mime-types "^2.1.12"
623 |
624 | fs.realpath@^1.0.0:
625 | version "1.0.0"
626 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
627 |
628 | fstream-ignore@^1.0.5:
629 | version "1.0.5"
630 | resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105"
631 | dependencies:
632 | fstream "^1.0.0"
633 | inherits "2"
634 | minimatch "^3.0.0"
635 |
636 | fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2:
637 | version "1.0.11"
638 | resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171"
639 | dependencies:
640 | graceful-fs "^4.1.2"
641 | inherits "~2.0.0"
642 | mkdirp ">=0.5 0"
643 | rimraf "2"
644 |
645 | functional-red-black-tree@^1.0.1:
646 | version "1.0.1"
647 | resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
648 |
649 | gauge@~2.7.3:
650 | version "2.7.4"
651 | resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
652 | dependencies:
653 | aproba "^1.0.3"
654 | console-control-strings "^1.0.0"
655 | has-unicode "^2.0.0"
656 | object-assign "^4.1.0"
657 | signal-exit "^3.0.0"
658 | string-width "^1.0.1"
659 | strip-ansi "^3.0.1"
660 | wide-align "^1.1.0"
661 |
662 | gcp-metadata@^0.3.0:
663 | version "0.3.1"
664 | resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-0.3.1.tgz#313814456e7c3d0eeb8f8b084b33579e886f829a"
665 | dependencies:
666 | extend "^3.0.0"
667 | retry-request "^3.0.0"
668 |
669 | gcp-metadata@^0.4.1:
670 | version "0.4.1"
671 | resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-0.4.1.tgz#64623b84175357cc119ad7a6aec759392a90a58b"
672 | dependencies:
673 | extend "^3.0.0"
674 | retry-request "^3.1.0"
675 |
676 | gcs-resumable-upload@^0.8.2:
677 | version "0.8.2"
678 | resolved "https://registry.yarnpkg.com/gcs-resumable-upload/-/gcs-resumable-upload-0.8.2.tgz#37df02470430395a789a637e72cabc80677ae964"
679 | dependencies:
680 | buffer-equal "^1.0.0"
681 | configstore "^3.0.0"
682 | google-auto-auth "^0.7.1"
683 | pumpify "^1.3.3"
684 | request "^2.81.0"
685 | stream-events "^1.0.1"
686 | through2 "^2.0.0"
687 |
688 | getpass@^0.1.1:
689 | version "0.1.7"
690 | resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
691 | dependencies:
692 | assert-plus "^1.0.0"
693 |
694 | glob@^7.0.5, glob@^7.1.2:
695 | version "7.1.2"
696 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
697 | dependencies:
698 | fs.realpath "^1.0.0"
699 | inflight "^1.0.4"
700 | inherits "2"
701 | minimatch "^3.0.4"
702 | once "^1.3.0"
703 | path-is-absolute "^1.0.0"
704 |
705 | globby@^7.1.1:
706 | version "7.1.1"
707 | resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680"
708 | dependencies:
709 | array-union "^1.0.1"
710 | dir-glob "^2.0.0"
711 | glob "^7.1.2"
712 | ignore "^3.3.5"
713 | pify "^3.0.0"
714 | slash "^1.0.0"
715 |
716 | google-auth-library@^0.10.0:
717 | version "0.10.0"
718 | resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-0.10.0.tgz#6e15babee85fd1dd14d8d128a295b6838d52136e"
719 | dependencies:
720 | gtoken "^1.2.1"
721 | jws "^3.1.4"
722 | lodash.noop "^3.0.1"
723 | request "^2.74.0"
724 |
725 | google-auth-library@^0.12.0:
726 | version "0.12.0"
727 | resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-0.12.0.tgz#a3fc6c296d00bb54e4d877ef581a05947330d07f"
728 | dependencies:
729 | gtoken "^1.2.3"
730 | jws "^3.1.4"
731 | lodash.isstring "^4.0.1"
732 | lodash.merge "^4.6.0"
733 | request "^2.81.0"
734 |
735 | google-auto-auth@^0.7.1:
736 | version "0.7.2"
737 | resolved "https://registry.yarnpkg.com/google-auto-auth/-/google-auto-auth-0.7.2.tgz#bf9352d5c4a0897bf31fd9c491028b765fbea71e"
738 | dependencies:
739 | async "^2.3.0"
740 | gcp-metadata "^0.3.0"
741 | google-auth-library "^0.10.0"
742 | request "^2.79.0"
743 |
744 | google-auto-auth@^0.8.0:
745 | version "0.8.2"
746 | resolved "https://registry.yarnpkg.com/google-auto-auth/-/google-auto-auth-0.8.2.tgz#928ee8954514a2ea179de8dd4e97f04d40d13d0a"
747 | dependencies:
748 | async "^2.3.0"
749 | gcp-metadata "^0.3.0"
750 | google-auth-library "^0.12.0"
751 | request "^2.79.0"
752 |
753 | google-auto-auth@^0.9.0:
754 | version "0.9.3"
755 | resolved "https://registry.yarnpkg.com/google-auto-auth/-/google-auto-auth-0.9.3.tgz#544c36da639890cf56040b0246859060f140715a"
756 | dependencies:
757 | async "^2.3.0"
758 | gcp-metadata "^0.4.1"
759 | google-auth-library "^0.12.0"
760 | request "^2.79.0"
761 |
762 | google-gax@^0.14.3:
763 | version "0.14.5"
764 | resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-0.14.5.tgz#b2c73c61df6cead94f90421d17f44ff161f623c7"
765 | dependencies:
766 | extend "^3.0.0"
767 | globby "^7.1.1"
768 | google-auto-auth "^0.9.0"
769 | google-proto-files "^0.14.1"
770 | grpc "~1.7.2"
771 | is-stream-ended "^0.1.0"
772 | lodash "^4.17.2"
773 | protobufjs "^6.8.0"
774 | readable-stream "^2.2.2"
775 | through2 "^2.0.3"
776 |
777 | google-p12-pem@^0.1.0:
778 | version "0.1.2"
779 | resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-0.1.2.tgz#33c46ab021aa734fa0332b3960a9a3ffcb2f3177"
780 | dependencies:
781 | node-forge "^0.7.1"
782 |
783 | google-proto-files@^0.14.1:
784 | version "0.14.2"
785 | resolved "https://registry.yarnpkg.com/google-proto-files/-/google-proto-files-0.14.2.tgz#958cffea7e8888e00b9a6c55ed1362c06b426f4c"
786 | dependencies:
787 | globby "^7.1.1"
788 | power-assert "^1.4.4"
789 | prettier "^1.10.2"
790 | protobufjs "^6.8.0"
791 |
792 | graceful-fs@^4.1.11, graceful-fs@^4.1.2:
793 | version "4.1.11"
794 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
795 |
796 | grpc@~1.7.2:
797 | version "1.7.3"
798 | resolved "https://registry.yarnpkg.com/grpc/-/grpc-1.7.3.tgz#c9d034324e2ec8a06cfaa577a044a116f96c8c90"
799 | dependencies:
800 | arguejs "^0.2.3"
801 | lodash "^4.15.0"
802 | nan "^2.0.0"
803 | node-pre-gyp "^0.6.39"
804 | protobufjs "^5.0.0"
805 |
806 | gtoken@^1.2.1, gtoken@^1.2.3:
807 | version "1.2.3"
808 | resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-1.2.3.tgz#5509571b8afd4322e124cf66cf68115284c476d8"
809 | dependencies:
810 | google-p12-pem "^0.1.0"
811 | jws "^3.0.0"
812 | mime "^1.4.1"
813 | request "^2.72.0"
814 |
815 | har-schema@^1.0.5:
816 | version "1.0.5"
817 | resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e"
818 |
819 | har-schema@^2.0.0:
820 | version "2.0.0"
821 | resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
822 |
823 | har-validator@~4.2.1:
824 | version "4.2.1"
825 | resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a"
826 | dependencies:
827 | ajv "^4.9.1"
828 | har-schema "^1.0.5"
829 |
830 | har-validator@~5.0.3:
831 | version "5.0.3"
832 | resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd"
833 | dependencies:
834 | ajv "^5.1.0"
835 | har-schema "^2.0.0"
836 |
837 | has-unicode@^2.0.0:
838 | version "2.0.1"
839 | resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
840 |
841 | hash-stream-validation@^0.2.1:
842 | version "0.2.1"
843 | resolved "https://registry.yarnpkg.com/hash-stream-validation/-/hash-stream-validation-0.2.1.tgz#ecc9b997b218be5bb31298628bb807869b73dcd1"
844 | dependencies:
845 | through2 "^2.0.0"
846 |
847 | hawk@3.1.3, hawk@~3.1.3:
848 | version "3.1.3"
849 | resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4"
850 | dependencies:
851 | boom "2.x.x"
852 | cryptiles "2.x.x"
853 | hoek "2.x.x"
854 | sntp "1.x.x"
855 |
856 | hawk@~6.0.2:
857 | version "6.0.2"
858 | resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038"
859 | dependencies:
860 | boom "4.x.x"
861 | cryptiles "3.x.x"
862 | hoek "4.x.x"
863 | sntp "2.x.x"
864 |
865 | hoek@2.x.x:
866 | version "2.16.3"
867 | resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
868 |
869 | hoek@4.x.x:
870 | version "4.2.0"
871 | resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d"
872 |
873 | http-parser-js@>=0.4.0:
874 | version "0.4.10"
875 | resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.10.tgz#92c9c1374c35085f75db359ec56cc257cbb93fa4"
876 |
877 | http-signature@~1.1.0:
878 | version "1.1.1"
879 | resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf"
880 | dependencies:
881 | assert-plus "^0.2.0"
882 | jsprim "^1.2.2"
883 | sshpk "^1.7.0"
884 |
885 | http-signature@~1.2.0:
886 | version "1.2.0"
887 | resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
888 | dependencies:
889 | assert-plus "^1.0.0"
890 | jsprim "^1.2.2"
891 | sshpk "^1.7.0"
892 |
893 | ignore@^3.3.5:
894 | version "3.3.7"
895 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021"
896 |
897 | imurmurhash@^0.1.4:
898 | version "0.1.4"
899 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
900 |
901 | indexof@0.0.1:
902 | version "0.0.1"
903 | resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d"
904 |
905 | inflight@^1.0.4:
906 | version "1.0.6"
907 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
908 | dependencies:
909 | once "^1.3.0"
910 | wrappy "1"
911 |
912 | inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
913 | version "2.0.3"
914 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
915 |
916 | ini@~1.3.0:
917 | version "1.3.5"
918 | resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
919 |
920 | invert-kv@^1.0.0:
921 | version "1.0.0"
922 | resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
923 |
924 | is-fullwidth-code-point@^1.0.0:
925 | version "1.0.0"
926 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
927 | dependencies:
928 | number-is-nan "^1.0.0"
929 |
930 | is-obj@^1.0.0:
931 | version "1.0.1"
932 | resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
933 |
934 | is-stream-ended@^0.1.0:
935 | version "0.1.3"
936 | resolved "https://registry.yarnpkg.com/is-stream-ended/-/is-stream-ended-0.1.3.tgz#a0473b267c756635486beedc7e3344e549d152ac"
937 |
938 | is-typedarray@~1.0.0:
939 | version "1.0.0"
940 | resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
941 |
942 | is@^3.0.1, is@^3.2.0, is@^3.2.1:
943 | version "3.2.1"
944 | resolved "https://registry.yarnpkg.com/is/-/is-3.2.1.tgz#d0ac2ad55eb7b0bec926a5266f6c662aaa83dca5"
945 |
946 | isarray@0.0.1:
947 | version "0.0.1"
948 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
949 |
950 | isarray@~1.0.0:
951 | version "1.0.0"
952 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
953 |
954 | isstream@~0.1.2:
955 | version "0.1.2"
956 | resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
957 |
958 | jsbn@~0.1.0:
959 | version "0.1.1"
960 | resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
961 |
962 | json-schema-traverse@^0.3.0:
963 | version "0.3.1"
964 | resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
965 |
966 | json-schema@0.2.3:
967 | version "0.2.3"
968 | resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
969 |
970 | json-stable-stringify@^1.0.1:
971 | version "1.0.1"
972 | resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af"
973 | dependencies:
974 | jsonify "~0.0.0"
975 |
976 | json-stringify-safe@~5.0.1:
977 | version "5.0.1"
978 | resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
979 |
980 | jsonify@~0.0.0:
981 | version "0.0.0"
982 | resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
983 |
984 | jsonwebtoken@8.1.0:
985 | version "8.1.0"
986 | resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.1.0.tgz#c6397cd2e5fd583d65c007a83dc7bb78e6982b83"
987 | dependencies:
988 | jws "^3.1.4"
989 | lodash.includes "^4.3.0"
990 | lodash.isboolean "^3.0.3"
991 | lodash.isinteger "^4.0.4"
992 | lodash.isnumber "^3.0.3"
993 | lodash.isplainobject "^4.0.6"
994 | lodash.isstring "^4.0.1"
995 | lodash.once "^4.0.0"
996 | ms "^2.0.0"
997 | xtend "^4.0.1"
998 |
999 | jsprim@^1.2.2:
1000 | version "1.4.1"
1001 | resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
1002 | dependencies:
1003 | assert-plus "1.0.0"
1004 | extsprintf "1.3.0"
1005 | json-schema "0.2.3"
1006 | verror "1.10.0"
1007 |
1008 | jwa@^1.1.4:
1009 | version "1.1.5"
1010 | resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.1.5.tgz#a0552ce0220742cd52e153774a32905c30e756e5"
1011 | dependencies:
1012 | base64url "2.0.0"
1013 | buffer-equal-constant-time "1.0.1"
1014 | ecdsa-sig-formatter "1.0.9"
1015 | safe-buffer "^5.0.1"
1016 |
1017 | jws@^3.0.0, jws@^3.1.4:
1018 | version "3.1.4"
1019 | resolved "https://registry.yarnpkg.com/jws/-/jws-3.1.4.tgz#f9e8b9338e8a847277d6444b1464f61880e050a2"
1020 | dependencies:
1021 | base64url "^2.0.0"
1022 | jwa "^1.1.4"
1023 | safe-buffer "^5.0.1"
1024 |
1025 | lcid@^1.0.0:
1026 | version "1.0.0"
1027 | resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
1028 | dependencies:
1029 | invert-kv "^1.0.0"
1030 |
1031 | lodash.includes@^4.3.0:
1032 | version "4.3.0"
1033 | resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
1034 |
1035 | lodash.isboolean@^3.0.3:
1036 | version "3.0.3"
1037 | resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
1038 |
1039 | lodash.isinteger@^4.0.4:
1040 | version "4.0.4"
1041 | resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
1042 |
1043 | lodash.isnumber@^3.0.3:
1044 | version "3.0.3"
1045 | resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
1046 |
1047 | lodash.isplainobject@^4.0.6:
1048 | version "4.0.6"
1049 | resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
1050 |
1051 | lodash.isstring@^4.0.1:
1052 | version "4.0.1"
1053 | resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
1054 |
1055 | lodash.merge@^4.6.0:
1056 | version "4.6.1"
1057 | resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54"
1058 |
1059 | lodash.noop@^3.0.1:
1060 | version "3.0.1"
1061 | resolved "https://registry.yarnpkg.com/lodash.noop/-/lodash.noop-3.0.1.tgz#38188f4d650a3a474258439b96ec45b32617133c"
1062 |
1063 | lodash.once@^4.0.0:
1064 | version "4.1.1"
1065 | resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
1066 |
1067 | lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2:
1068 | version "4.17.5"
1069 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"
1070 |
1071 | log-driver@^1.2.5:
1072 | version "1.2.5"
1073 | resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.5.tgz#7ae4ec257302fd790d557cb10c97100d857b0056"
1074 |
1075 | long@^3.2.0, long@~3:
1076 | version "3.2.0"
1077 | resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b"
1078 |
1079 | make-dir@^1.0.0:
1080 | version "1.1.0"
1081 | resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.1.0.tgz#19b4369fe48c116f53c2af95ad102c0e39e85d51"
1082 | dependencies:
1083 | pify "^3.0.0"
1084 |
1085 | methmeth@^1.1.0:
1086 | version "1.1.0"
1087 | resolved "https://registry.yarnpkg.com/methmeth/-/methmeth-1.1.0.tgz#e80a26618e52f5c4222861bb748510bd10e29089"
1088 |
1089 | mime-db@~1.30.0:
1090 | version "1.30.0"
1091 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01"
1092 |
1093 | mime-types@^2.0.8, mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.7:
1094 | version "2.1.17"
1095 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a"
1096 | dependencies:
1097 | mime-db "~1.30.0"
1098 |
1099 | mime@^1.4.1:
1100 | version "1.6.0"
1101 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
1102 |
1103 | minimatch@^3.0.0, minimatch@^3.0.4:
1104 | version "3.0.4"
1105 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
1106 | dependencies:
1107 | brace-expansion "^1.1.7"
1108 |
1109 | minimist@0.0.8:
1110 | version "0.0.8"
1111 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
1112 |
1113 | minimist@^1.2.0:
1114 | version "1.2.0"
1115 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
1116 |
1117 | "mkdirp@>=0.5 0", mkdirp@^0.5.1:
1118 | version "0.5.1"
1119 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
1120 | dependencies:
1121 | minimist "0.0.8"
1122 |
1123 | modelo@^4.2.0:
1124 | version "4.2.3"
1125 | resolved "https://registry.yarnpkg.com/modelo/-/modelo-4.2.3.tgz#b278588a4db87fc1e5107ae3a277c0876f38d894"
1126 |
1127 | ms@2.0.0:
1128 | version "2.0.0"
1129 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
1130 |
1131 | ms@^2.0.0:
1132 | version "2.1.1"
1133 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
1134 |
1135 | nan@^2.0.0:
1136 | version "2.8.0"
1137 | resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a"
1138 |
1139 | node-forge@0.7.1, node-forge@^0.7.1:
1140 | version "0.7.1"
1141 | resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.1.tgz#9da611ea08982f4b94206b3beb4cc9665f20c300"
1142 |
1143 | node-pre-gyp@^0.6.39:
1144 | version "0.6.39"
1145 | resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649"
1146 | dependencies:
1147 | detect-libc "^1.0.2"
1148 | hawk "3.1.3"
1149 | mkdirp "^0.5.1"
1150 | nopt "^4.0.1"
1151 | npmlog "^4.0.2"
1152 | rc "^1.1.7"
1153 | request "2.81.0"
1154 | rimraf "^2.6.1"
1155 | semver "^5.3.0"
1156 | tar "^2.2.1"
1157 | tar-pack "^3.4.0"
1158 |
1159 | nopt@^4.0.1:
1160 | version "4.0.1"
1161 | resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
1162 | dependencies:
1163 | abbrev "1"
1164 | osenv "^0.1.4"
1165 |
1166 | npmlog@^4.0.2:
1167 | version "4.1.2"
1168 | resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
1169 | dependencies:
1170 | are-we-there-yet "~1.1.2"
1171 | console-control-strings "~1.1.0"
1172 | gauge "~2.7.3"
1173 | set-blocking "~2.0.0"
1174 |
1175 | number-is-nan@^1.0.0:
1176 | version "1.0.1"
1177 | resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
1178 |
1179 | oauth-sign@~0.8.1, oauth-sign@~0.8.2:
1180 | version "0.8.2"
1181 | resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
1182 |
1183 | object-assign@^4.1.0:
1184 | version "4.1.1"
1185 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
1186 |
1187 | object-keys@^1.0.0, object-keys@^1.0.8:
1188 | version "1.0.11"
1189 | resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d"
1190 |
1191 | once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0:
1192 | version "1.4.0"
1193 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
1194 | dependencies:
1195 | wrappy "1"
1196 |
1197 | optjs@~3.2.2:
1198 | version "3.2.2"
1199 | resolved "https://registry.yarnpkg.com/optjs/-/optjs-3.2.2.tgz#69a6ce89c442a44403141ad2f9b370bd5bb6f4ee"
1200 |
1201 | os-homedir@^1.0.0:
1202 | version "1.0.2"
1203 | resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
1204 |
1205 | os-locale@^1.4.0:
1206 | version "1.4.0"
1207 | resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9"
1208 | dependencies:
1209 | lcid "^1.0.0"
1210 |
1211 | os-tmpdir@^1.0.0:
1212 | version "1.0.2"
1213 | resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
1214 |
1215 | osenv@^0.1.4:
1216 | version "0.1.4"
1217 | resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644"
1218 | dependencies:
1219 | os-homedir "^1.0.0"
1220 | os-tmpdir "^1.0.0"
1221 |
1222 | path-is-absolute@^1.0.0:
1223 | version "1.0.1"
1224 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
1225 |
1226 | path-type@^3.0.0:
1227 | version "3.0.0"
1228 | resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
1229 | dependencies:
1230 | pify "^3.0.0"
1231 |
1232 | performance-now@^0.2.0:
1233 | version "0.2.0"
1234 | resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"
1235 |
1236 | performance-now@^2.1.0:
1237 | version "2.1.0"
1238 | resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
1239 |
1240 | pify@^3.0.0:
1241 | version "3.0.0"
1242 | resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
1243 |
1244 | power-assert-context-formatter@^1.0.7:
1245 | version "1.1.1"
1246 | resolved "https://registry.yarnpkg.com/power-assert-context-formatter/-/power-assert-context-formatter-1.1.1.tgz#edba352d3ed8a603114d667265acce60d689ccdf"
1247 | dependencies:
1248 | core-js "^2.0.0"
1249 | power-assert-context-traversal "^1.1.1"
1250 |
1251 | power-assert-context-reducer-ast@^1.0.7:
1252 | version "1.1.2"
1253 | resolved "https://registry.yarnpkg.com/power-assert-context-reducer-ast/-/power-assert-context-reducer-ast-1.1.2.tgz#484a99e26f4973ff8832e5c5cc756702e6094174"
1254 | dependencies:
1255 | acorn "^4.0.0"
1256 | acorn-es7-plugin "^1.0.12"
1257 | core-js "^2.0.0"
1258 | espurify "^1.6.0"
1259 | estraverse "^4.2.0"
1260 |
1261 | power-assert-context-traversal@^1.1.1:
1262 | version "1.1.1"
1263 | resolved "https://registry.yarnpkg.com/power-assert-context-traversal/-/power-assert-context-traversal-1.1.1.tgz#88cabca0d13b6359f07d3d3e8afa699264577ed9"
1264 | dependencies:
1265 | core-js "^2.0.0"
1266 | estraverse "^4.1.0"
1267 |
1268 | power-assert-formatter@^1.3.1:
1269 | version "1.4.1"
1270 | resolved "https://registry.yarnpkg.com/power-assert-formatter/-/power-assert-formatter-1.4.1.tgz#5dc125ed50a3dfb1dda26c19347f3bf58ec2884a"
1271 | dependencies:
1272 | core-js "^2.0.0"
1273 | power-assert-context-formatter "^1.0.7"
1274 | power-assert-context-reducer-ast "^1.0.7"
1275 | power-assert-renderer-assertion "^1.0.7"
1276 | power-assert-renderer-comparison "^1.0.7"
1277 | power-assert-renderer-diagram "^1.0.7"
1278 | power-assert-renderer-file "^1.0.7"
1279 |
1280 | power-assert-renderer-assertion@^1.0.7:
1281 | version "1.1.1"
1282 | resolved "https://registry.yarnpkg.com/power-assert-renderer-assertion/-/power-assert-renderer-assertion-1.1.1.tgz#cbfc0e77e0086a8f96af3f1d8e67b9ee7e28ce98"
1283 | dependencies:
1284 | power-assert-renderer-base "^1.1.1"
1285 | power-assert-util-string-width "^1.1.1"
1286 |
1287 | power-assert-renderer-base@^1.1.1:
1288 | version "1.1.1"
1289 | resolved "https://registry.yarnpkg.com/power-assert-renderer-base/-/power-assert-renderer-base-1.1.1.tgz#96a650c6fd05ee1bc1f66b54ad61442c8b3f63eb"
1290 |
1291 | power-assert-renderer-comparison@^1.0.7:
1292 | version "1.1.1"
1293 | resolved "https://registry.yarnpkg.com/power-assert-renderer-comparison/-/power-assert-renderer-comparison-1.1.1.tgz#d7439d97d85156be4e30a00f2fb5a72514ce3c08"
1294 | dependencies:
1295 | core-js "^2.0.0"
1296 | diff-match-patch "^1.0.0"
1297 | power-assert-renderer-base "^1.1.1"
1298 | stringifier "^1.3.0"
1299 | type-name "^2.0.1"
1300 |
1301 | power-assert-renderer-diagram@^1.0.7:
1302 | version "1.1.2"
1303 | resolved "https://registry.yarnpkg.com/power-assert-renderer-diagram/-/power-assert-renderer-diagram-1.1.2.tgz#655f8f711935a9b6d541b86327654717c637a986"
1304 | dependencies:
1305 | core-js "^2.0.0"
1306 | power-assert-renderer-base "^1.1.1"
1307 | power-assert-util-string-width "^1.1.1"
1308 | stringifier "^1.3.0"
1309 |
1310 | power-assert-renderer-file@^1.0.7:
1311 | version "1.1.1"
1312 | resolved "https://registry.yarnpkg.com/power-assert-renderer-file/-/power-assert-renderer-file-1.1.1.tgz#a37e2bbd178ccacd04e78dbb79c92fe34933c5e7"
1313 | dependencies:
1314 | power-assert-renderer-base "^1.1.1"
1315 |
1316 | power-assert-util-string-width@^1.1.1:
1317 | version "1.1.1"
1318 | resolved "https://registry.yarnpkg.com/power-assert-util-string-width/-/power-assert-util-string-width-1.1.1.tgz#be659eb7937fdd2e6c9a77268daaf64bd5b7c592"
1319 | dependencies:
1320 | eastasianwidth "^0.1.1"
1321 |
1322 | power-assert@^1.4.4:
1323 | version "1.4.4"
1324 | resolved "https://registry.yarnpkg.com/power-assert/-/power-assert-1.4.4.tgz#9295ea7437196f5a601fde420f042631186d7517"
1325 | dependencies:
1326 | define-properties "^1.1.2"
1327 | empower "^1.2.3"
1328 | power-assert-formatter "^1.3.1"
1329 | universal-deep-strict-equal "^1.2.1"
1330 | xtend "^4.0.0"
1331 |
1332 | prettier@^1.10.2:
1333 | version "1.10.2"
1334 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.10.2.tgz#1af8356d1842276a99a5b5529c82dd9e9ad3cc93"
1335 |
1336 | process-nextick-args@~1.0.6:
1337 | version "1.0.7"
1338 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
1339 |
1340 | protobufjs@^5.0.0:
1341 | version "5.0.2"
1342 | resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-5.0.2.tgz#59748d7dcf03d2db22c13da9feb024e16ab80c91"
1343 | dependencies:
1344 | ascli "~1"
1345 | bytebuffer "~5"
1346 | glob "^7.0.5"
1347 | yargs "^3.10.0"
1348 |
1349 | protobufjs@^6.8.0:
1350 | version "6.8.4"
1351 | resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.8.4.tgz#183f90d1c4aca5f6b34a79eaedd0d89ad21f603b"
1352 | dependencies:
1353 | "@protobufjs/aspromise" "^1.1.2"
1354 | "@protobufjs/base64" "^1.1.2"
1355 | "@protobufjs/codegen" "^2.0.4"
1356 | "@protobufjs/eventemitter" "^1.1.0"
1357 | "@protobufjs/fetch" "^1.1.0"
1358 | "@protobufjs/float" "^1.0.2"
1359 | "@protobufjs/inquire" "^1.1.0"
1360 | "@protobufjs/path" "^1.1.2"
1361 | "@protobufjs/pool" "^1.1.0"
1362 | "@protobufjs/utf8" "^1.1.0"
1363 | "@types/long" "^3.0.32"
1364 | "@types/node" "^8.5.5"
1365 | long "^3.2.0"
1366 |
1367 | pump@^2.0.0:
1368 | version "2.0.1"
1369 | resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909"
1370 | dependencies:
1371 | end-of-stream "^1.1.0"
1372 | once "^1.3.1"
1373 |
1374 | pumpify@^1.3.3:
1375 | version "1.4.0"
1376 | resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.4.0.tgz#80b7c5df7e24153d03f0e7ac8a05a5d068bd07fb"
1377 | dependencies:
1378 | duplexify "^3.5.3"
1379 | inherits "^2.0.3"
1380 | pump "^2.0.0"
1381 |
1382 | punycode@^1.4.1:
1383 | version "1.4.1"
1384 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
1385 |
1386 | qs@~6.4.0:
1387 | version "6.4.0"
1388 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
1389 |
1390 | qs@~6.5.1:
1391 | version "6.5.1"
1392 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
1393 |
1394 | rc@^1.1.7:
1395 | version "1.2.5"
1396 | resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.5.tgz#275cd687f6e3b36cc756baa26dfee80a790301fd"
1397 | dependencies:
1398 | deep-extend "~0.4.0"
1399 | ini "~1.3.0"
1400 | minimist "^1.2.0"
1401 | strip-json-comments "~2.0.1"
1402 |
1403 | readable-stream@^2.0.0, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2:
1404 | version "2.3.3"
1405 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c"
1406 | dependencies:
1407 | core-util-is "~1.0.0"
1408 | inherits "~2.0.3"
1409 | isarray "~1.0.0"
1410 | process-nextick-args "~1.0.6"
1411 | safe-buffer "~5.1.1"
1412 | string_decoder "~1.0.3"
1413 | util-deprecate "~1.0.1"
1414 |
1415 | readable-stream@~1.0.32:
1416 | version "1.0.34"
1417 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
1418 | dependencies:
1419 | core-util-is "~1.0.0"
1420 | inherits "~2.0.1"
1421 | isarray "0.0.1"
1422 | string_decoder "~0.10.x"
1423 |
1424 | request@2.81.0:
1425 | version "2.81.0"
1426 | resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
1427 | dependencies:
1428 | aws-sign2 "~0.6.0"
1429 | aws4 "^1.2.1"
1430 | caseless "~0.12.0"
1431 | combined-stream "~1.0.5"
1432 | extend "~3.0.0"
1433 | forever-agent "~0.6.1"
1434 | form-data "~2.1.1"
1435 | har-validator "~4.2.1"
1436 | hawk "~3.1.3"
1437 | http-signature "~1.1.0"
1438 | is-typedarray "~1.0.0"
1439 | isstream "~0.1.2"
1440 | json-stringify-safe "~5.0.1"
1441 | mime-types "~2.1.7"
1442 | oauth-sign "~0.8.1"
1443 | performance-now "^0.2.0"
1444 | qs "~6.4.0"
1445 | safe-buffer "^5.0.1"
1446 | stringstream "~0.0.4"
1447 | tough-cookie "~2.3.0"
1448 | tunnel-agent "^0.6.0"
1449 | uuid "^3.0.0"
1450 |
1451 | request@^2.72.0, request@^2.74.0, request@^2.79.0, request@^2.81.0, request@^2.83.0:
1452 | version "2.83.0"
1453 | resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356"
1454 | dependencies:
1455 | aws-sign2 "~0.7.0"
1456 | aws4 "^1.6.0"
1457 | caseless "~0.12.0"
1458 | combined-stream "~1.0.5"
1459 | extend "~3.0.1"
1460 | forever-agent "~0.6.1"
1461 | form-data "~2.3.1"
1462 | har-validator "~5.0.3"
1463 | hawk "~6.0.2"
1464 | http-signature "~1.2.0"
1465 | is-typedarray "~1.0.0"
1466 | isstream "~0.1.2"
1467 | json-stringify-safe "~5.0.1"
1468 | mime-types "~2.1.17"
1469 | oauth-sign "~0.8.2"
1470 | performance-now "^2.1.0"
1471 | qs "~6.5.1"
1472 | safe-buffer "^5.1.1"
1473 | stringstream "~0.0.5"
1474 | tough-cookie "~2.3.3"
1475 | tunnel-agent "^0.6.0"
1476 | uuid "^3.1.0"
1477 |
1478 | retry-request@^3.0.0, retry-request@^3.1.0, retry-request@^3.3.1:
1479 | version "3.3.1"
1480 | resolved "https://registry.yarnpkg.com/retry-request/-/retry-request-3.3.1.tgz#fb71276235a617e97551e9be737ab5b91591fb9e"
1481 | dependencies:
1482 | request "^2.81.0"
1483 | through2 "^2.0.0"
1484 |
1485 | rimraf@2, rimraf@^2.5.1, rimraf@^2.6.1:
1486 | version "2.6.2"
1487 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
1488 | dependencies:
1489 | glob "^7.0.5"
1490 |
1491 | safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
1492 | version "5.1.1"
1493 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
1494 |
1495 | semver@^5.3.0:
1496 | version "5.5.0"
1497 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
1498 |
1499 | set-blocking@~2.0.0:
1500 | version "2.0.0"
1501 | resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
1502 |
1503 | signal-exit@^3.0.0, signal-exit@^3.0.2:
1504 | version "3.0.2"
1505 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
1506 |
1507 | slash@^1.0.0:
1508 | version "1.0.0"
1509 | resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
1510 |
1511 | snakeize@^0.1.0:
1512 | version "0.1.0"
1513 | resolved "https://registry.yarnpkg.com/snakeize/-/snakeize-0.1.0.tgz#10c088d8b58eb076b3229bb5a04e232ce126422d"
1514 |
1515 | sntp@1.x.x:
1516 | version "1.0.9"
1517 | resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198"
1518 | dependencies:
1519 | hoek "2.x.x"
1520 |
1521 | sntp@2.x.x:
1522 | version "2.1.0"
1523 | resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8"
1524 | dependencies:
1525 | hoek "4.x.x"
1526 |
1527 | split-array-stream@^1.0.0:
1528 | version "1.0.3"
1529 | resolved "https://registry.yarnpkg.com/split-array-stream/-/split-array-stream-1.0.3.tgz#d2b75a8e5e0d824d52fdec8b8225839dc2e35dfa"
1530 | dependencies:
1531 | async "^2.4.0"
1532 | is-stream-ended "^0.1.0"
1533 |
1534 | sshpk@^1.7.0:
1535 | version "1.13.1"
1536 | resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3"
1537 | dependencies:
1538 | asn1 "~0.2.3"
1539 | assert-plus "^1.0.0"
1540 | dashdash "^1.12.0"
1541 | getpass "^0.1.1"
1542 | optionalDependencies:
1543 | bcrypt-pbkdf "^1.0.0"
1544 | ecc-jsbn "~0.1.1"
1545 | jsbn "~0.1.0"
1546 | tweetnacl "~0.14.0"
1547 |
1548 | stream-events@^1.0.1:
1549 | version "1.0.2"
1550 | resolved "https://registry.yarnpkg.com/stream-events/-/stream-events-1.0.2.tgz#abf39f66c0890a4eb795bc8d5e859b2615b590b2"
1551 | dependencies:
1552 | stubs "^3.0.0"
1553 |
1554 | stream-shift@^1.0.0:
1555 | version "1.0.0"
1556 | resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952"
1557 |
1558 | string-format-obj@^1.0.0, string-format-obj@^1.1.0:
1559 | version "1.1.1"
1560 | resolved "https://registry.yarnpkg.com/string-format-obj/-/string-format-obj-1.1.1.tgz#c7612ca4e2ad923812a81db192dc291850aa1f65"
1561 |
1562 | string-width@^1.0.1, string-width@^1.0.2:
1563 | version "1.0.2"
1564 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
1565 | dependencies:
1566 | code-point-at "^1.0.0"
1567 | is-fullwidth-code-point "^1.0.0"
1568 | strip-ansi "^3.0.0"
1569 |
1570 | string_decoder@~0.10.x:
1571 | version "0.10.31"
1572 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
1573 |
1574 | string_decoder@~1.0.3:
1575 | version "1.0.3"
1576 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab"
1577 | dependencies:
1578 | safe-buffer "~5.1.0"
1579 |
1580 | stringifier@^1.3.0:
1581 | version "1.3.0"
1582 | resolved "https://registry.yarnpkg.com/stringifier/-/stringifier-1.3.0.tgz#def18342f6933db0f2dbfc9aa02175b448c17959"
1583 | dependencies:
1584 | core-js "^2.0.0"
1585 | traverse "^0.6.6"
1586 | type-name "^2.0.1"
1587 |
1588 | stringstream@~0.0.4, stringstream@~0.0.5:
1589 | version "0.0.5"
1590 | resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878"
1591 |
1592 | strip-ansi@^3.0.0, strip-ansi@^3.0.1:
1593 | version "3.0.1"
1594 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
1595 | dependencies:
1596 | ansi-regex "^2.0.0"
1597 |
1598 | strip-json-comments@~2.0.1:
1599 | version "2.0.1"
1600 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
1601 |
1602 | stubs@^3.0.0:
1603 | version "3.0.0"
1604 | resolved "https://registry.yarnpkg.com/stubs/-/stubs-3.0.0.tgz#e8d2ba1fa9c90570303c030b6900f7d5f89abe5b"
1605 |
1606 | tar-pack@^3.4.0:
1607 | version "3.4.1"
1608 | resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f"
1609 | dependencies:
1610 | debug "^2.2.0"
1611 | fstream "^1.0.10"
1612 | fstream-ignore "^1.0.5"
1613 | once "^1.3.3"
1614 | readable-stream "^2.1.4"
1615 | rimraf "^2.5.1"
1616 | tar "^2.2.1"
1617 | uid-number "^0.0.6"
1618 |
1619 | tar@^2.2.1:
1620 | version "2.2.1"
1621 | resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1"
1622 | dependencies:
1623 | block-stream "*"
1624 | fstream "^1.0.2"
1625 | inherits "2"
1626 |
1627 | through2@^2.0.0, through2@^2.0.3:
1628 | version "2.0.3"
1629 | resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be"
1630 | dependencies:
1631 | readable-stream "^2.1.5"
1632 | xtend "~4.0.1"
1633 |
1634 | tough-cookie@~2.3.0, tough-cookie@~2.3.3:
1635 | version "2.3.3"
1636 | resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561"
1637 | dependencies:
1638 | punycode "^1.4.1"
1639 |
1640 | traverse@^0.6.6:
1641 | version "0.6.6"
1642 | resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137"
1643 |
1644 | tunnel-agent@^0.6.0:
1645 | version "0.6.0"
1646 | resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
1647 | dependencies:
1648 | safe-buffer "^5.0.1"
1649 |
1650 | tweetnacl@^0.14.3, tweetnacl@~0.14.0:
1651 | version "0.14.5"
1652 | resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
1653 |
1654 | type-name@^2.0.1:
1655 | version "2.0.2"
1656 | resolved "https://registry.yarnpkg.com/type-name/-/type-name-2.0.2.tgz#efe7d4123d8ac52afff7f40c7e4dec5266008fb4"
1657 |
1658 | typedarray@^0.0.6:
1659 | version "0.0.6"
1660 | resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
1661 |
1662 | uid-number@^0.0.6:
1663 | version "0.0.6"
1664 | resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
1665 |
1666 | unique-string@^1.0.0:
1667 | version "1.0.0"
1668 | resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a"
1669 | dependencies:
1670 | crypto-random-string "^1.0.0"
1671 |
1672 | universal-deep-strict-equal@^1.2.1:
1673 | version "1.2.2"
1674 | resolved "https://registry.yarnpkg.com/universal-deep-strict-equal/-/universal-deep-strict-equal-1.2.2.tgz#0da4ac2f73cff7924c81fa4de018ca562ca2b0a7"
1675 | dependencies:
1676 | array-filter "^1.0.0"
1677 | indexof "0.0.1"
1678 | object-keys "^1.0.0"
1679 |
1680 | util-deprecate@~1.0.1:
1681 | version "1.0.2"
1682 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
1683 |
1684 | uuid@^3.0.0, uuid@^3.1.0:
1685 | version "3.2.1"
1686 | resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14"
1687 |
1688 | verror@1.10.0:
1689 | version "1.10.0"
1690 | resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
1691 | dependencies:
1692 | assert-plus "^1.0.0"
1693 | core-util-is "1.0.2"
1694 | extsprintf "^1.2.0"
1695 |
1696 | websocket-driver@>=0.5.1:
1697 | version "0.7.0"
1698 | resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.0.tgz#0caf9d2d755d93aee049d4bdd0d3fe2cca2a24eb"
1699 | dependencies:
1700 | http-parser-js ">=0.4.0"
1701 | websocket-extensions ">=0.1.1"
1702 |
1703 | websocket-extensions@>=0.1.1:
1704 | version "0.1.3"
1705 | resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29"
1706 |
1707 | wide-align@^1.1.0:
1708 | version "1.1.2"
1709 | resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710"
1710 | dependencies:
1711 | string-width "^1.0.2"
1712 |
1713 | window-size@^0.1.4:
1714 | version "0.1.4"
1715 | resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876"
1716 |
1717 | wrap-ansi@^2.0.0:
1718 | version "2.1.0"
1719 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
1720 | dependencies:
1721 | string-width "^1.0.1"
1722 | strip-ansi "^3.0.1"
1723 |
1724 | wrappy@1:
1725 | version "1.0.2"
1726 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
1727 |
1728 | write-file-atomic@^2.0.0:
1729 | version "2.3.0"
1730 | resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.3.0.tgz#1ff61575c2e2a4e8e510d6fa4e243cce183999ab"
1731 | dependencies:
1732 | graceful-fs "^4.1.11"
1733 | imurmurhash "^0.1.4"
1734 | signal-exit "^3.0.2"
1735 |
1736 | xdg-basedir@^3.0.0:
1737 | version "3.0.0"
1738 | resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
1739 |
1740 | xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1:
1741 | version "4.0.1"
1742 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
1743 |
1744 | y18n@^3.2.0:
1745 | version "3.2.1"
1746 | resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
1747 |
1748 | yargs@^3.10.0:
1749 | version "3.32.0"
1750 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995"
1751 | dependencies:
1752 | camelcase "^2.0.1"
1753 | cliui "^3.0.3"
1754 | decamelize "^1.1.1"
1755 | os-locale "^1.4.0"
1756 | string-width "^1.0.1"
1757 | window-size "^0.1.4"
1758 | y18n "^3.2.0"
1759 |
--------------------------------------------------------------------------------
/firebase-authentication/01-intro.md:
--------------------------------------------------------------------------------
1 | # Introduction: Why Firebase Authentication?
2 |
3 | You need Firebase Authentication for two reasons:
4 |
5 | 1. Firebase Security Rules rely on Firebase Authentication
6 | 2. Firebase Authentication is ridiculously easy to use
7 |
8 | ## Firebase Auth + Security Rules
9 |
10 | Firebase needs a security system. In a traditional database you provide your own security using your
11 | API server. Since Firebase **Is** the API server, it needs a programmable way to control read and
12 | write access to your data. When users use your client apps to authenticate with Firebase
13 | Authentication they receive a [JSON Web Tokens](https://jwt.io/) that will identify them to
14 | Firebase's Security Rules system. We'll cover this more later.
15 |
16 | ## Ease Of Use
17 |
18 | Have you ever implemented your own auth system? Yes? Then you know how challenging it can be. If
19 | not... then take my word for it and use an off-the-shelf system. Firebase Authentication provides
20 | the following auth methods:
21 |
22 | * Email/password
23 | * Phone
24 | * OAuth 2
25 | * Google
26 | * Facebook
27 | * Twitter
28 | * Github
29 | * Anonymous
30 | * Custom auth
31 |
32 | 
33 |
34 | All of these methods use [JSON Web Tokens](https://jwt.io/), also known as JWTs.
35 |
36 | > JWT is pronounced the same as the English word "jot".
37 |
38 | We'll cover JWTs later. Don't worry. They're relatively simple.
39 |
40 | ### Email/Password
41 |
42 | Email/password auth is exactly what it sounds like. You register an email address and a password
43 | with Firebase and it keeps track of your user account.
44 |
45 | ### Phone
46 |
47 | Google [acquired Twitter Digits](https://firebase.googleblog.com/2017/01/FabricJoinsGoogle17.html)
48 | in early 2017 and rolled their phone authentication into Firebase Authentication. This means that
49 | with minimum fuss you can implement a full SMS-based phone auth flow. This is particularly great for
50 | mobile web apps. Phone authentication is the preferred auth method for many users, especially those
51 | outside of the United States.
52 |
53 | ### OAuth 2 (Google, Facebook, Twitter, Github)
54 |
55 | OAuth is the fastest auth method, because it relies on accounts that your users already have. Most
56 | everyone has either Google, Facebook or Twitter, and developers love Github. OAuth 2 is also the
57 | easiest auth flow to implement.
58 |
59 | And all of the OAuth providers support
60 | [multi-factor authentication](https://en.wikipedia.org/wiki/Multi-factor_authentication), which we
61 | should all be using.
62 |
63 | ### Anonymous Auth
64 |
65 | You may want to interact with a client that hasn't authenticated. If you're developing a shopping
66 | cart feature for your app, you may want users to add items to their carts before they've created an
67 | account. This is where anonymous auth comes in.
68 |
69 | Without some sort of authentication, there's no way for your server to know who it's talking to.
70 | Firebase's data is either entirely open or requires authentication. Any private transaction between
71 | the Firebase database and a client app requires authentication or it will be public and could be
72 | intercepted by a malicious third party.
73 |
74 | There are situations in which you might make your data publicly accessible; however, user
75 | transactions with your database are unlikely to fit this model... and that's where anonymous auth
76 | comes in.
77 |
78 | We won't cover anonymous auth any further except to show how dead simple it is to execute:
79 |
80 | ```javascript
81 | firebase
82 | .auth()
83 | .signInAnonymously()
84 | .then(successHandler, errorHandler);
85 | ```
86 |
87 | ### Custom Tokens
88 |
89 | Would you like to use a different authentication method with Firebase? That's not a problem. We
90 | won't cover custom tokens here except to say that you can use your auth server to mint Firebase auth
91 | tokens that you can give to your client applications. Those tokens will allow your client apps to
92 | authenticate with Firebase using whatever JWT you chose to create on your server.
93 |
94 | ## Link Multiple Auth Providers
95 |
96 | This is another topic we won't cover here. Just be aware that you can easily
97 | [link multiple auth providers to a single account](https://firebase.google.com/docs/auth/web/account-linking).
98 |
99 | For example, your user might register an email/password account and you could prompt her to link her
100 | account to Google and Facebook. Your client app can make a couple of easy calls to Firebase
101 | Authentication to initiate the linking procedure, and now your user can sign in with Google,
102 | Facebook, Twitter or Github.
103 |
104 | Alternatively, if your user signed up with an OAuth account, you can prompt her to register a linked
105 | email/password combination.
106 |
--------------------------------------------------------------------------------
/firebase-authentication/02-configuration.md:
--------------------------------------------------------------------------------
1 | # Configuration: Let's get started!
2 |
3 | Your first step is to visit your Firebase Dashboard and select Develop > Authentication from the
4 | left nav bar.
5 |
6 | Now select the **SIGN-IN METHOD** tab and start enabling sign-in methods.
7 |
8 | 
9 |
10 | Email/Password, Phone, Google and Anonymous auth can be enabled with a quick button click. The
11 | third-party providers--Facebook, Twitter and Github--will require a little more ceremony. You'll
12 | need to follow the instructions in the Firebase docs to register your app with the third-party
13 | provider. We'll cover the Facebook flow below.
14 |
15 | But quick! Before you get distracted, scroll down a bit and notice that Firebase requires you to
16 | approve the domains that you want to use with Firebase Authentication. Your Firebase Hosting domain
17 | will be there by default. So will 127.0.0.1 and localhost. Add any extra domains you hope to use.
18 |
19 | Also notice the **Advanced** options:
20 |
21 | * One account per email address
22 | * Manage sign-up quota
23 |
24 | I personally like to enable multiple auth methods for each email address. That enables my users to
25 | sign in with Google one day and Facebook the next. My app's business logic will notice the matching
26 | email addresses and provide them the same account data.
27 |
28 | Multiple accounts per email is harder to secure than requiring one account per email. If you require
29 | one account per email you can still prompt users to link accounts their other
30 | Google/Facebook/Twitter/Github accounts once they've logged in.
31 |
32 | I've just noticed that my users tend to forget how they logged in last time, so I like to
33 | automatically link accounts if possible. But yeah... that's extra work on my end, and it's possible
34 | that an attacker could steal a victim's email, add it to their Facebook account and use Facebook to
35 | log into my app :(
36 |
37 | Managing the sign-up quota is useful if you find that your app is getting spammed by fake sign-up
38 | attempts. This is rare :)
39 |
40 | ## Add a third-party OAuth provider
41 |
42 | We'll cover the Facebook flow below. Facebook may alter their developer site, so check the
43 | [Firebase docs](https://firebase.google.com/docs/auth/web/facebook-login) if you get confused.
44 |
45 | 1. [Create a developer account](https://developers.facebook.com/) with Facebook or log in to your
46 | existing account.
47 | 2. Create a new Facebook app.
48 | 3. Copy your App ID and App Secret
49 | 4. Paste your App ID and App Secret into the Firebase Dashboard
50 | 5. Copy the OAuth redirect URI from your Firebase Dashboard
51 | 6. Set up Facebook Login on Facebook's developer site
52 | 7. Paste your OAuth redirect URI from step 5 into Facebook's **Valid OAuth redirect URIs** field
53 |
54 | ### Screenshots: Add Facebook OAuth
55 |
56 | 
57 |
58 | 
59 |
60 | 
61 |
62 | 
63 |
64 | 
65 |
66 | 
67 |
68 | # Add Firebase Auth to your Web App
69 |
70 | This is the easy part.
71 |
72 | The Firebase SDK is typically added to a web page using a `
124 |
125 |
126 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
127 | ```
128 |
129 | 
130 |
131 | 
132 |
--------------------------------------------------------------------------------
/firebase-authentication/03-implementation.md:
--------------------------------------------------------------------------------
1 | # Implementation: The easy part?
2 |
3 | All example code can be found in my
4 | [Quiver repo](https://github.com/deltaepsilon/quiver/tree/master/packages/firebase-authentication)
5 | on Github. I've called the project `firebase-authentication`. The relevant Firebase Authentication
6 | code can be found in
7 | [auth-service.js](https://github.com/deltaepsilon/quiver/blob/master/packages/firebase-authentication/src/services/auth.service.js).
8 |
9 | > These examples use
10 | > [JavaScript ES2015 destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)
11 | > and single-parameter (monad) functions. I like to pass in a single object with named attributes,
12 | > then destructure those attributes in the method signature. This is all personal preference, so
13 | > don't let it distract you.
14 |
15 | ## Overview
16 |
17 | The example code uses [Preact](https://github.com/developit/preact) for client-side templating, but
18 | don't get distracted by that. The relevant code for Firebase is all standard ES2015 JavaScript.
19 |
20 | ### Auth Service
21 |
22 | We'll start by reviewing
23 | [auth-service.js](https://github.com/deltaepsilon/quiver/blob/master/packages/firebase-authentication/src/services/auth.service.js).
24 |
25 | I'm using [Visual Studio Code](https://code.visualstudio.com/) as my editor/IDE, and you'll notice a
26 | lot of code folding in my example images. Code folding is a way to visually collapse blocks of code
27 | in the IDE so that they don't distract us.
28 |
29 | ### Function Signatures
30 |
31 | **AuthService** has 10 methods that use `firebase.auth()`.
32 |
33 | * `onAuthStateChanged`
34 | * `signOut`
35 | * `currentUserDelete`
36 | * `signInWithEmailAndPassword`
37 | * `createUserWithEmailAndPassword`
38 | * `sendPasswordResetEmail`
39 | * `signInWithPhoneNumber`
40 | * `signInWithPopup`
41 | * `signInWithRedirect`
42 | * `getRecaptchaVerifier`
43 |
44 | 
45 |
46 | 
47 |
48 | **AuthService** takes 4 functions as external dependencies
49 |
50 | * fire
51 | * handleError
52 | * changeView
53 | * clearInputs
54 |
55 | These external dependencies encapsulate business logic that's irrelevant to our examples, so don't
56 | worry about them.
57 |
58 | **AuthService** also uses `window.firebase` and `firebase.auth()` to bootstrap itself. This assumes
59 | that Firebase has already been attached to the `window` object via script tags on the page.
60 |
61 | Finally, **AuthService** creates a `providersMap` using the `firebase.auth` prototype. These
62 | providers will be used for OAuth login in the `signInWithPopup` and `signInWithRedirect` methods.
63 |
64 | 
65 |
66 | ### onAuthStateChanged
67 |
68 | The `onAuthStateChanged` method is the crux of Firebase Authentication. It's used to register a
69 | callback that gets called whenever the page's authentication state changes.
70 |
71 | > Note: Firebase Authentication uses `localStorage` to save authentication tokens across user
72 | > sessions.
73 |
74 | If the page loads and there are no authentication tokens in `localStorage`, then the callback is
75 | called with a `null` argument. Otherwise, if Firebase Authentication has previously authenticated on
76 | the page and saved a token to `localStorage`, then `onAuthStateChanged` is called with Firebase
77 | Authentication's `currentUser`.
78 |
79 | The `currentUser` is the JWT that Firebase Authentication is using for the session.
80 |
81 | The example below shows that `AuthService`'s exported `onAuthStateChanged` function is merely a
82 | wrapper for `window.firebase.auth().onAuthStateChanged`. You don't need to wrap `onAuthStateChanged`
83 | in your own code, but it was helpful with this service architecture to isolate `AuthService` from
84 | the component that's using it.
85 |
86 | The example below has two files, `auth.service.js` and `index.js`. `index.js` is the code for the
87 | component that's using `AuthService`. Notice in the example below that `initAuthService` creates a
88 | new `AuthService` and passes in the relevant dependencies. Next it passes a callback into
89 | `this.authService.onAuthStateChanged`. This callback contains application-specific business logic
90 | which we can ignore. Just note that `currentUser` will be ``null` if authentication fails and will
91 | contain the JWT if authentication succeeds.
92 |
93 | 
94 |
95 | ### signOut & delete
96 |
97 | Signing out is easy enough. We called `firebase.auth()` at the top of the file and assigned the auth
98 | object to the variable `auth`, so all we need to do is call `auth.signOut()` and our session will be
99 | closed and our `onAuthStateChanged` callback will be called with a `null` currentUser.
100 |
101 | Deleting our currentUser is a bit trickier, because we need to get the currentUser first.
102 | currentUser is an attribute on our `auth` object. It can be accessed directly directly from
103 | `window.firebase.auth().currentUser`. The `currentUser` object has a `delete` method that returns a
104 | promise.
105 |
106 | 
107 |
108 | ### Email/Password Auth
109 |
110 | There are two email/password-specific functions with the same method signature:
111 |
112 | * `auth.signInWithEmailAndPassword(email, password)`
113 | * `auth.createUserWithEmailAndPassword(email, password)`
114 |
115 | Both methods return promises, and you should definitely use `.catch` blocks to handle errors from
116 | both methods. The error objects have a `code` attribute that you'll use to handle the errors.
117 |
118 | `signInWithEmailAndPassword` throws two codes worth handling:
119 |
120 | * `auth/user-not-found`
121 | * `auth/wrong-password`
122 |
123 | There's one **gotcha** here, and it's with `auth/wrong-password`. If you offer login with an OAuth
124 | provider--Google/Facebook/Twitter/Github--you'll likely have users that log in with the OAuth
125 | provider, sign out, and then try to log in with the same email address.
126 |
127 | In this case there will be an account under that email address, but it won't have a password
128 | associated with it. This login attempt will throw an `auth/wrong-password` error, so you'll need to
129 | decide on your own business logic in this case. Do you want to suggest that they reset their
130 | password, register a new email/password account, or remind them to try signing in with an OAuth
131 | provider? It's up to you!
132 |
133 | `createUserWithEmailAndPassword` throws one error:
134 |
135 | * `auth/email-already-in-use`
136 |
137 | Note that `auth/email-already-in-use` will only be thrown if a user already has an OAuth account
138 | **AND** you've limited users to one account per email address. You can change this setting in your
139 | Firebase Dashboard under **Authentication > Sign-In Method > Advanced > One account per email
140 | address > CHANGE**
141 |
142 | 
143 |
144 | There's one more email/password method that we've missed: `sendPasswordResetEmail(email)`
145 |
146 | It's simple enough. It sends an email with a password reset link. You can modify the password reset
147 | email on your Dashboard under **Authentication > Templates > Password reset**.
148 |
149 | 
150 |
151 | ### Phone Auth
152 |
153 | Phone auth is a little trickier to implement due to the need for a recaptcha element on the page.
154 | This can get a little complicated, so you'll want to review the
155 | [Firebase docs](https://firebase.google.com/docs/auth/web/phone-auth) before creating your own
156 | implementation.
157 |
158 | The gist is that you create an element on your page to hold the recaptcha element and give Firebase
159 | the `id` for that element. Firebase Authentication will automatically inject a recaptcha box into
160 | the element and will request that the user complete a recaptcha test before signing in with a phone
161 | number.
162 |
163 | The trick is that you can specify that the recaptcha should be `invisible` and use the `id` of the
164 | button that you use to trigger the phone auth. Firebase will then use Google's recaptcha system to
165 | do mouse tracking on the button to determine if the user is human. If the recaptcha mouse-tracking
166 | test is inconclusive, Firebase will open a recaptcha modal in the window.
167 |
168 | You'll need to register your `RecaptchaVerifier` when you render your phone login form. You'll also
169 | need to pass the resulting `RecaptchaVerifier` object into `signInwithPhoneNumber(phoneNumber,
170 | recaptchaVerifier)`.
171 |
172 | 
173 |
174 | There's another **gotcha** with `signInWithPhoneNumber`: be careful how you format the phone number.
175 | The format that's successful for me is `+1 1234567890`, where `+1` is a country code, followed by
176 | a space and then the phone number. The following example uses the variable name `callingCode` for
177 | the country code.
178 |
179 | Notice in the example how I handle a successful call to `signInWithPhoneNumber` in the `.then(...)`
180 | block. The `.then(...)` block gets called with a `confirmationResult` object that has a `.confirm`
181 | method on it. `.confirm` returns a promise, and you'll need to catch any errors with a `.catch(...)`
182 | block. You'll call `confirmationResult.confirm(code)` with the confirmation code that will be sent
183 | to your user's phone via SMS.
184 |
185 | If `.confirm` is passed a valid code, then the callback we registered earlier with
186 | `auth.onAuthStateChanged(cb)` will be called with the new currentUser JWT. If the code is wrong, the
187 | `.confirm` call will throw an error with the code `auth/invalid-verification-code`.
188 |
189 | 
190 |
191 | ## Phone Auth Review
192 |
193 | Phone auth is tricky for two reasons:
194 |
195 | 1. Recaptcha
196 | 2. Saving the `confirmationResult` and calling it later
197 |
198 | You'll likely need to fuss with the recaptcha a bit to get it working smoothly with your user
199 | interface. Experiment with different size options and use the
200 | [Firebase docs](https://firebase.google.com/docs/auth/web/phone-auth) as your guide. I personally
201 | like to attach an invisible recaptcha to my **SEND SMS** button, but you may find the visible
202 | captcha to be more reliable.
203 |
204 | I also found that I needed to pass in a callback when registering a new `RecaptchaVerifier`, even if
205 | that callback was an empty function. Experiment with it and be patient :)
206 |
207 | Second, calling `auth.signInwithPhoneNumber` can be tricky because you'll need to save the resulting
208 | `confirmationResult` using `.then(confirmationResult => ...)`. In my example I used an external
209 | function called `setConfirm` and passed it a function that takes a code and passes it into
210 | `confirmationResult.confirm`. This is a common JavaScript pattern, but it may be hard to read if
211 | you're less familiar with JavaScript. Pay close attention to lines 64 and 65 of the example.
212 |
213 | `setConfirm` is an external function that I passed into `AuthService` from a UI component.
214 | `setConfirm` takes a callback function and saves it to a variable for later use. When the user has
215 | entered a confirmation code into that component's form, the component calls the callback function
216 | with the code. The callback function has wrapped a call to
217 | `confirmationResult.confirm(code).catch(...)`.
218 |
219 | ### OAuth with Popup or Redirect
220 |
221 | OAuth is the easy part. We can call one of two methods, and the callback we registered earlier with
222 | `auth.onAuthStateChanged` will handle the result. And that's it.
223 |
224 | * `auth.signInWithPopup(provider)`
225 | * `auth.signInWithRedirect(provider)`
226 |
227 | Just make sure to pass in a valid provider. Our example created a map of all possible providers at
228 | the top of the file and assigned it to `providersMap`, but you can create the provider object inline
229 | if you prefer.
230 |
231 | 
232 |
--------------------------------------------------------------------------------
/firebase-authentication/04-outro.md:
--------------------------------------------------------------------------------
1 | # Outro: Maybe it wasn't that easy :)
2 |
3 | We covered email/password, phone and OAuth authentication methods.
4 |
5 | We did not cover anonymous authentication or custom authentication.
6 |
7 | ## Anonymous & Custom Authentication
8 |
9 | Most apps will not need anonymous or custom authentication, and I don't recommend using them unless
10 | you absolutely need them.
11 |
12 | You'll know that you need anonymous auth if you need secure database transactions with an
13 | otherwise-unauthenticated user, or if you need to maintain a user session without authenticating
14 | your user.
15 |
16 | You'll only use custom auth if you need to integrate a Firebase client with a non-Firebase
17 | authentication system. There's no problem with doing this... just don't waste your time on it if
18 | it's not necessary. The entire point of using Firebase is that it's an app platform. Yes, you can
19 | use each part of the platform separately, but that kinda defeats the purpose.
20 |
21 | ## OAuth is the best
22 |
23 | Seriously. Use OAuth providers, especially Google. OAuth providers let your users benefit from
24 | multi-factor authentication, and they de-risk your own code. Sure, holding onto an email and
25 | password for a few seconds before you pass them to Firebase and delete them forever isn't
26 | particularly risky... but why waste the effort when you can use users' existing OAuth provider
27 | accounts?
28 |
29 | ## Know your users
30 |
31 |
32 | I just urged you to use OAuth. Well, maybe don't use OAuth. Make sure you know your users. Talk to
33 | them. Are they social-media obsessed power users who prefer Twitter OAuth, or are they phone-first
34 | users who may not have any other accounts and would prefer a quick SMS auth experience?
35 |
36 | Yeah, it's easy to overload your users with **ALL OF THE AUTHS**, but sometimes a simple
37 | email/password form is more effective. Find out. Ask your users. And if you're too lazy to ask,
38 | maybe start with OAuth and track page abandonment analytics?
39 |
40 | Auth sign-in flows can be make-or-break for an app. Don't fail because of your sign-in flow.
41 |
42 |
43 |
--------------------------------------------------------------------------------
/firebase-authentication/05-notes.md:
--------------------------------------------------------------------------------
1 | ### Phone Auth
2 |
3 | * `signInWithPhoneNumber('+1 1234567890', new
4 | window.firebase.auth.RecaptchaVerifier('recaptcha-id-from-element', {size: 'invisible', callback:
5 | () => {}}))`
6 | * `window.firebase.auth().signInWithPhoneNumber(phoneNumber,
7 | recaptchaVerifier).then(confirmationResult => { myConfirmFunction = code =>
8 | confirmationResult.confirm(code) })`
9 |
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/1000-auth-methods.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/1000-auth-methods.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/1100-sign-in-method-enabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/1100-sign-in-method-enabled.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/1200-auth-domain-enabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/1200-auth-domain-enabled.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/2000-facebook-read-docs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/2000-facebook-read-docs.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/2050-facebook-create-app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/2050-facebook-create-app.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/2100-facebook-add-login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/2100-facebook-add-login.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/2200-facebook-copy-app-id-and-secret.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/2200-facebook-copy-app-id-and-secret.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/2300-facebook-paste-id-and-secret.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/2300-facebook-paste-id-and-secret.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/2400-facebook-paste-redirect-url.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/2400-facebook-paste-redirect-url.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/3000-go-do-dashboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/3000-go-do-dashboard.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/3100-copy-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/3100-copy-config.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/3300-auth-domain-initialized.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/3300-auth-domain-initialized.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/3400-init-js.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/3400-init-js.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/3500-folder-structure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/3500-folder-structure.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/4000-auth-service.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/4000-auth-service.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/4100-auth-service-return.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/4100-auth-service-return.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/4200-method-signature.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/4200-method-signature.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/4300-onAuthStateChanged.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/4300-onAuthStateChanged.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/4350-sign-out-delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/4350-sign-out-delete.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/4400-email-password.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/4400-email-password.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/4500-password-reset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/4500-password-reset.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/4600-phone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/4600-phone.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/4700-recaptcha-verifier.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/4700-recaptcha-verifier.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/4800-oauth-sign-in.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/4800-oauth-sign-in.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/5000-login-options.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/5000-login-options.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/5100-input-email.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/5100-input-email.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/5200-input-password.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/5200-input-password.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/5300-logged-in.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/5300-logged-in.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/5400-bad-password.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/5400-bad-password.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/5500-input-email-two.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/5500-input-email-two.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/5600-input-password-two.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/5600-input-password-two.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/5700-register-email.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/5700-register-email.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/5800-input-phone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/5800-input-phone.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/5900-confirm-phone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/5900-confirm-phone.png
--------------------------------------------------------------------------------
/firebase-authentication/screenshots/5950-oauth-google.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/how-to-firebase/tutorials/6dc00133e85118c64fbd7553efb587e5e0764cc2/firebase-authentication/screenshots/5950-oauth-google.png
--------------------------------------------------------------------------------
/firebase-cloud-functions/.firebaserc:
--------------------------------------------------------------------------------
1 | {
2 | "projects": {
3 | "default": "quiver-two"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/firebase-cloud-functions/README.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 |
3 | [Firebase Cloud Functions](https://firebase.googleblog.com/2017/03/introducing-cloud-functions-for-firebase.html) are Google's serverless solution for Firebase apps. They can be used as the (R)eactor functions for [FIRE Stack](https://howtofirebase.com/fire-stack-4195a13daf96) app architecture. If you've developed with firebase-queue, AWS Lambda or some other Functions-as-a-Service architecture, Firebase Cloud Functions should feel natural... just a lot slicker and easier to use :)
4 |
5 | If you're wondering where to start... well, read on my friend.
6 |
7 | ### FIRE Stack
8 |
9 | [FIRE Stack architecture](https://howtofirebase.com/fire-stack-4195a13daf96) replaces the typical REST API with its endpoint and HTTP calls with standalone functions——written by you and running on Firebase's infrastructure—-that react to changes in your app and do anything that you can do in Node.js or Java.
10 |
11 | As of this writing, there are six types of triggers:
12 |
13 | 1. [Firebase Realtime Database](https://firebase.google.com/docs/functions/database-events) triggers
14 | 2. [Firebase Authentication](https://firebase.google.com/docs/functions/auth-events) triggers
15 | 3. [Firebase Analytics](https://firebase.google.com/docs/functions/analytics-events) triggers
16 | 4. [Cloud Storage](https://firebase.google.com/docs/functions/gcp-storage-events) triggers
17 | 5. [HTTP](https://firebase.google.com/docs/functions/http-events) triggers
18 | 6. [Cloud Pub/Sub](https://firebase.google.com/docs/functions/pubsub-events) triggers
19 |
20 | You can read the docs on each of those triggers for the full rundown. They're not hard to use, although they can be tricky to test. I'm going to start with Authentication and Firebase Realtime Database triggers. If you can get those working, you shouldn't have trouble with the other event types.
21 |
22 | ### Node Environment
23 |
24 | Cloud Functions supports Node.js LTS releases. The current release is v6.9.1, but [check the docs](https://cloud.google.com/functions/docs/writing/) to make sure that you're developing against the freshest-possible version of Node.js.
25 |
26 | If you need help jumping between Node.js versions, check out [n](https://github.com/tj/n) for fast version switching.
27 |
28 | To get started, I'm running ```n 6.9.1``` to switch to v6.9.1.
29 |
30 | ### Authentication Triggers
31 |
32 | Authentication triggers track creation and deletion of Firebase Authentication users. That's about it. Here are the examples from [the docs](https://firebase.google.com/docs/functions/auth-events).
33 |
34 | ```javascript
35 | exports.sendWelcomeEmail = functions.auth.user().onCreate(event => {
36 | const user = event.data;
37 | const email = user.email;
38 | // ...
39 | });
40 |
41 | exports.sendByeEmail = functions.auth.user().onDelete(event => {
42 | const user = event.data;
43 | const email = user.email;
44 | // ...
45 | });
46 | ```
47 |
48 | That's really all there is to it. ```event.data``` is the user data from your newly minted or deleted (currentUser JWT (JavaScript Web Token))[https://firebase.google.com/docs/auth/users].
49 |
50 | If you want to access your Realtime Database, you can't get it from the event :( Fortunately, ```functions.config().firebase``` contains your initialization details, so you can use ```firebase-admin``` to create whatever refs you need.
51 |
52 | ```javascript
53 | functions.auth.user().onCreate(event => {
54 | const functions = require('firebase-functions');
55 | const admin = require('firebase-admin');
56 | const config = functions.config();
57 |
58 | admin.initializeApp(config.firebase);
59 |
60 | const user = event.data;
61 | const userRef = admin.database().ref('/users').child(user.uid);
62 |
63 | return userRef.update(user);
64 | });
65 | ```
66 |
67 | ### Realtime Database Triggers
68 |
69 | This is likely the trigger that you need for [FIRE Stack architecture](https://howtofirebase.com/fire-stack-4195a13daf96).
70 |
71 | It's also the most complicated trigger, because of all of the attributes on the event itself. You need to read up on these attributes. I'll only be summarizing a few here.
72 |
73 | First, [scan the docs](https://firebase.google.com/docs/reference/functions/functions.Event#properties) about event properties. Focus on the following:
74 |
75 | - ```event.data```
76 | - ```event.params```
77 |
78 | Next, read a bit more carefully through the docs on [DeltaSnapshot](https://firebase.google.com/docs/reference/functions/functions.database.DeltaSnapshot), a.k.a. ```event.data```. This is the crux of Realtime Database events. Pay attention to everything, but read the following word-for-word.
79 |
80 | - ```DeltaSnapshot.adminRef```
81 | - ```DeltaSnapshot.current```
82 | - ```DeltaSnapshot.key```
83 | - ```DeltaSnapshot.previous```
84 | - ```DeltaSnapshot.ref```
85 | - ```DeltaSnapshot.val()```
86 | - ```DeltaSnapshot.toJSON()```
87 | - ```DeltaSnapshot.numChildren()```
88 |
89 | You're back already? Have you understood the docs? If so, GREAT! If not, for shame! Go back and read 'em :)
90 |
91 | Once you understand the ```event``` object, database triggers aren't tough to figure out. I have an architectural pattern where I like to track user logins. It's kind of an important metric, and it's a good opportunity to update the user's account. I have my client app push data to ```/queues/current-user/{uid}```, something like this...
92 |
93 | ```javascript
94 | // Client-side code, in my case a web browser
95 | firebase.auth().onAuthStateChanged(function (user) {
96 | this.user = user; // Set my local copy of the 'user' object
97 | if (user) {
98 | this.displayName = user.displayName;
99 | this.photoURL = user.photoURL;
100 | var userRef = firebase.database().ref('queues/login').child(user.uid);
101 |
102 | userRef.remove()
103 | .then(function () {
104 | return userRef.set({
105 | email: user.email,
106 | emailVerified: user.emailVerified,
107 | photoURL: user.photoURL,
108 | displayName: user.displayName,
109 | timestamp: new Date().toString()
110 | });
111 | });
112 | }
113 | }.bind(this));
114 | ```
115 |
116 | Then I register a Firebase Function to listen to ```queues/login/{uid}``` and do whatever I need.
117 |
118 | ```javascript
119 | const function = require('firebase-functions');
120 |
121 | functions.database.ref('queues/login/{uid}').onWrite(event => {
122 | const config = functions.config();
123 | // functions.config() returns your environment variables.
124 | // In this case I have an array of my admin users' email addresses in accessControlLists.adminUsers
125 | // It looks like ['chris@chrisesplin.com', 'some-other-admin@chrisesplin.com']
126 | const adminUsersString = config.['access-control-lists']['admin-users'];
127 | const adminUsers = adminUsersString.split(',');
128 | const user = event.data.val();
129 | const userRef = event.data.adminRef.root.child('users').child(event.params.uid);
130 |
131 | if (!user) return Promise.resolve();
132 |
133 | user.lastLogin = Date.now();
134 |
135 | if (adminUsers.includes(user.email)) {
136 | user.isAdmin = true;
137 | }
138 |
139 | return userRef.update(user).then(() => {
140 | return event.data.ref.remove();
141 | });
142 | };
143 | });
144 | ```
145 |
146 | ### Use event.data.adminRef and event.data.ref
147 |
148 | ```event.data.adminRef``` is a ref with full admin privileges over your entire database. ```event.data.ref``` is a ref as well, but is has the same authentication permissions as the user who triggered the event. This is ***insanely useful***. It enables you to impersonate a user from within your function, security-rules restrictions and all!
149 |
150 | Also, if you try to do something "admin-like" from ```event.data.ref```, don't be surprised when you get weird ***permission denied*** errors in your Cloud Functions logs.
151 |
152 | ### Use event.params
153 |
154 | In the earlier example I registered my ***onWrite*** event like so:
155 |
156 | ```javascript
157 | functions.database.ref('queues/login/{uid}').onWrite(event => {...});
158 | ```
159 | Notice the wildcard ```{uid}```. That wildcard ends up as ```event.params.uid```. So if write something to ```/queues/login/fake-uid-123```, then ```event.params.uid``` will be ```'fake-uid-123'```. Pretty simple.
160 |
161 | But the plot thickens, because you can do crazy stuff like
162 |
163 | ```javascript
164 | functions.database.ref('/queues/{queueType}/{uid}').onWrite(event => {...});
165 | ```
166 | See that! You can use multiple wildcards in a path! I don't know how ***many*** wildcards you can use, and I've never used more than two... but go hog-wild and let me know if you find the limits!
167 |
168 | ### Use environment variables!
169 |
170 | The docs on [environment variables](https://firebase.google.com/docs/functions/config-env) show how you can use ```firebase-tools``` to set your functions environment variables from the command line. Basically, you install ```firebase-tools``` globally with ```yarn global add firebase-tools``` (or ```npm install -g firebase-tools```). Then you use the CLI to do the work.
171 |
172 | ```bash
173 | firebase functions:config:set access-control-lists.admin-users="chris@chrisesplin.com,someone-else@chrisesplin.com"
174 | ```
175 |
176 | Notice that I used kebab case instead of camel case. Functions config does not allow uppercase characters in attribute keys. So ```accessControlLists.adminUsers```, which is what I'd like to type here, ***will not work!!!*** 👹
177 |
178 | Also notice that I passed in a comma-delimited string. Stick to strings. You can [coerce](https://github.com/getify/You-Dont-Know-JS/blob/master/types%20%26%20grammar/ch4.md) these strings to other data types once you're in the function... but don't try to get cute with booleans, numbers or arrays. They'll all get wrapped in double-quotes.
179 |
180 | ### Require your dependencies within each function
181 |
182 | You'll want to define most of your require statements, i.e., ```const quiverFunctions = require('quiver-functions')```, within the function itself. This will run the ```require``` function every time the function is run.
183 |
184 | This won't bite you often, but I've run into a few situations where my functions wouldn't deploy until I moved a ```require``` call into the function. I suspect it has something to do with how the functions are triggered on Google's end. If you see error messages when attempting to deploy functions, this tip might help.
185 |
186 | ***Good***
187 | ```javascript
188 | exports.sendEmail = functions.auth.user().onCreate(event => {
189 | const mailgun = require('mailgun-js');
190 | // ...
191 | });
192 | ```
193 |
194 | ***Not As Awesome***
195 | ```javascript
196 | const mailgun = require('mailgun-js');
197 | exports.sendEmail = functions.auth.user().onCreate(event => {
198 | // ...
199 | });
200 | ```
201 |
202 | ### Testing
203 |
204 | I'm using [Jasmine](https://jasmine.github.io/2.0/node.html) for my Node.js tests. It's easy to get started on your own projects. Just do the following:
205 |
206 | > I'm using Yarn instead of NPM these days. NPM works almost the same, but let's be honest, Yarn's better :)
207 | > Here are [the docs on installing Yarn](https://yarnpkg.com/lang/en/docs/install/).
208 | > If you're on OS X, you should be using [Homebrew](https://brew.sh/) for your own sanity.
209 | > If you're serious about dev on OS X, and you're not using Homebrew, take a few minutes to get it set up. Thank me later.
210 | > Installing Yarn with Homebrew is as easy as ```brew update && brew install yarn```.
211 |
212 | 1. Install Jasmine globally: ```yarn global add jasmine```
213 | 2. Install Jasmine in your project: ```yarn add jasmine```
214 | 3. Initialize Jasmin: ```jasmine init```
215 | 4. Edit ```./spec/support/jasmine.json``` to make sure that the ```spec_dir``` and ```spec_files``` will pick up your tests.
216 |
217 | You can run the test in this repo by simply installing and running ```jasmine``` at the top-level of the repo. Otherwise, you can ```cd functions && yarn test``` to get the same result. Note that you'll need to copy ```/functions/config.json.dist``` to ```/functions/config.json``` and edit the file so that it contains your Firebase details. I'm not checking my ```service-account.json``` file into source control :)
218 |
219 | ### NEXT STEPS
220 |
221 | 1. Long-running processes
222 | 2. Cron jobs
223 | 3. All of the new event types
224 |
225 |
--------------------------------------------------------------------------------
/firebase-cloud-functions/context.json:
--------------------------------------------------------------------------------
1 | {
2 | "eventId": "eb6H4zSgQABVKKifYOV24u0+Z0k=",
3 | "timestamp": "2018-03-20T11:27:57.893Z",
4 | "eventType": "google.firebase.database.ref.create",
5 | "resource": {
6 | "service": "firebaseio.com",
7 | "name":
8 | "projects/_/instances/how-to-firebase-tutorials/refs/authenticated/react-chat/Default Chat Room/-L829b5QxEoINb1ft39v"
9 | },
10 | "authType": "USER",
11 | "auth": {
12 | "uid": "DNhXq7T4igfchsZDIhxBWbRquF03",
13 | "token": {
14 | "name": "Chris Esplin",
15 | "email_verified": true,
16 | "email": "chris@quiver.is",
17 | "exp": 1521548871,
18 | "user_id": "DNhXq7T4igfchsZDIhxBWbRquF03",
19 | "picture":
20 | "https://lh4.googleusercontent.com/-ly98tZeA6F0/AAAAAAAAAAI/AAAAAAAAADk/G-1n2ID9bOw/photo.jpg",
21 | "iat": 1521545271,
22 | "sub": "DNhXq7T4igfchsZDIhxBWbRquF03",
23 | "aud": "how-to-firebase-tutorials",
24 | "auth_time": 1521545271,
25 | "iss": "https://securetoken.google.com/how-to-firebase-tutorials",
26 | "firebase": {
27 | "identities": { "google.com": ["116279478330828791198"], "email": ["chris@quiver.is"] },
28 | "sign_in_provider": "google.com"
29 | }
30 | }
31 | },
32 | "params": { "room": "Default Chat Room", "key": "-L829b5QxEoINb1ft39v" }
33 | }
34 |
--------------------------------------------------------------------------------
/firebase-cloud-functions/firebase.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/firebase-cloud-functions/functions/config.json.dist:
--------------------------------------------------------------------------------
1 | {
2 | "firebase": {
3 | "databaseURL": "https://quiver-two.firebaseio.com",
4 | "storageBucket": "quiver-two.appspot.com",
5 | "apiKey": "123123123",
6 | "authDomain": "quiver-two.firebaseapp.com",
7 | "serviceAccount": "/Users/quiver/.gcloud/quiver-two-service-account.json"
8 | },
9 | "config": {
10 | "token": "1/ABCDEFG",
11 | "project": "quiver-two"
12 | },
13 | "models": {
14 | "queues": {
15 | "current-user": "quiver-functions/queues/current-user"
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/firebase-cloud-functions/functions/index.js:
--------------------------------------------------------------------------------
1 | const functions = require('firebase-functions');
2 | const admin = require('firebase-admin');
3 |
4 | const quiverFunctions = require('quiver-functions');
5 | const OnCreate = quiverFunctions.OnCreate;
6 | const Login = quiverFunctions.Login;
7 |
8 | const config = functions.config();
9 | admin.initializeApp(config.firebase);
10 |
11 | const onCreate = new OnCreate({
12 | usersPath: 'quiver-functions/users',
13 | database: admin.database()
14 | });
15 | exports.onCreate = functions.auth.user().onCreate(onCreate.getFunction());
16 |
17 | const login = new Login({
18 | usersPath: 'quiver-functions/users',
19 | adminUsers: ['chris@chrisesplin.com']
20 | });
21 | exports.login = functions.database.ref('quiver-functions/queues/current-user/{uid}').onWrite(login.getFunction());
22 |
--------------------------------------------------------------------------------
/firebase-cloud-functions/functions/index.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const admin = require('firebase-admin');
3 | const config = require('./config.json');
4 | const quiverFunctions = require('quiver-functions');
5 | const Login = quiverFunctions.Login;
6 | const OnCreate = quiverFunctions.OnCreate;
7 | const mocks = quiverFunctions.mocks;
8 | const _ = require('lodash');
9 |
10 | admin.initializeApp({
11 | credential: admin.credential.cert(config.firebase.serviceAccount),
12 | databaseURL: config.firebase.databaseURL
13 | });
14 |
15 | const database = admin.database();
16 | const rootRef = database.ref('quiver-functions/test');
17 |
18 | const mockUser = {
19 | uid: 'fake-uid',
20 | email: 'chris@chrisesplin.com',
21 | password: '123456',
22 | displayName: 'Chris Esplin',
23 | photoURL: 'https://lh4.googleusercontent.com/-ly98tZeA6F0/AAAAAAAAAAI/AAAAAAAAADk/G-1n2ID9bOw/photo.jpg?sz=64',
24 | disabled: false,
25 | emailVerified: false
26 | };
27 |
28 | function cleanUp(done) {
29 | return rootRef.remove().then(done);
30 | };
31 |
32 | beforeEach(done => cleanUp(done));
33 | afterAll(done => cleanUp(done));
34 |
35 | describe('Login', () => {
36 | let fakeUser, userRef, loginEvent, loginFunction;
37 | beforeEach(() => {
38 | const login = new Login({
39 | usersPath: '/quiver-functions/test/users',
40 | adminUsers: ['chris@chrisesplin.com']
41 | });
42 | const loginQueueRef = rootRef.child('/queues/login');
43 |
44 | fakeUser = _.cloneDeep(mockUser);
45 | userRef = rootRef.child('users').child(fakeUser.uid);
46 | loginEvent = new mocks.MockDBEvent(loginQueueRef, { uid: fakeUser.uid }, fakeUser);
47 | loginFunction = login.getFunction();
48 | });
49 |
50 | it('should process a user login queue item', done => {
51 | loginFunction(loginEvent).then(() => userRef.once('value')).then(snap => {
52 | const user = snap.val();
53 |
54 | expect(snap.key).toEqual(fakeUser.uid);
55 | done();
56 | });
57 | });
58 | });
59 |
60 | describe('onCreate', () => {
61 | let fakeUser, userRef, onCreateEvent, onCreateFunction;
62 | beforeEach(() => {
63 | const onCreate = new OnCreate({
64 | usersPath: '/quiver-functions/test/users',
65 | database: database
66 | });
67 |
68 | fakeUser = _.cloneDeep(mockUser);
69 | userRef = rootRef.child('users').child(fakeUser.uid);
70 | onCreateEvent = new mocks.MockAuthEvent(fakeUser);
71 | onCreateFunction = onCreate.getFunction();
72 | });
73 |
74 | it('should process auth onCreate', done => {
75 |
76 | onCreateFunction(onCreateEvent).then(() => userRef.once('value')).then(snap => {
77 | const user = snap.val();
78 |
79 | expect(user.uid).toEqual(fakeUser.uid);
80 | done();
81 | });
82 | });
83 | });
84 |
--------------------------------------------------------------------------------
/firebase-cloud-functions/functions/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "functions",
3 | "description": "Cloud Functions for Firebase",
4 | "dependencies": {
5 | "firebase-admin": "^4.1.2",
6 | "firebase-functions": "^0.5",
7 | "quiver-functions": "^1.0.11"
8 | },
9 | "private": true,
10 | "scripts": {
11 | "test": "cd .. && jasmine",
12 | "deploy": "cd .. && firebase deploy --only functions"
13 | },
14 | "version": "1.0.0",
15 | "main": "index.js",
16 | "license": "MIT",
17 | "devDependencies": {
18 | "lodash": "^4.17.4",
19 | "jasmine": "^2.5.3"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/firebase-cloud-functions/snap.val.json:
--------------------------------------------------------------------------------
1 | {
2 | "displayName": "Chris Esplin",
3 | "email": "chris@quiver.is",
4 | "message": "Let's try this again",
5 | "photoURL":
6 | "https://lh4.googleusercontent.com/-ly98tZeA6F0/AAAAAAAAAAI/AAAAAAAAADk/G-1n2ID9bOw/photo.jpg",
7 | "uid": "DNhXq7T4igfchsZDIhxBWbRquF03"
8 | }
9 |
--------------------------------------------------------------------------------
/firebase-cloud-functions/spec/support/jasmine.json:
--------------------------------------------------------------------------------
1 | {
2 | "spec_dir": "",
3 | "spec_files": [
4 | "functions/index.spec.js"
5 | ],
6 | "helpers": [
7 | "helpers/**/*.js"
8 | ],
9 | "stopSpecOnExpectationFailure": false,
10 | "random": false
11 | }
12 |
--------------------------------------------------------------------------------
/starter-project/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "env",
4 | "preact"
5 | ]
6 | }
--------------------------------------------------------------------------------
/starter-project/.firebaserc:
--------------------------------------------------------------------------------
1 | {
2 | "projects": {
3 | "default": "how-to-firebase-tutorials"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/starter-project/database.rules.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "users": {
4 | "$uid": {
5 | ".read": "auth.uid === $uid || auth.token.admin === true",
6 | ".write": "auth.uid === $uid || auth.token.admin === true"
7 | }
8 | },
9 | "userOwned": {
10 | "$objectType": {
11 | "$uid": {
12 | ".read": "auth.uid === $uid || auth.token.admin === true",
13 | ".write": "auth.uid === $uid || auth.token.admin === true"
14 | }
15 | }
16 | },
17 | "userReadable": {
18 | "$objectType": {
19 | "$uid": {
20 | ".read": "auth.uid === $uid || auth.token.admin === true",
21 | ".write": "auth.token.admin === true"
22 | }
23 | }
24 | },
25 | "userWriteable": {
26 | "$objectType": {
27 | "$uid": {
28 | ".read": "auth.token.admin === true",
29 | ".write": "auth.uid === $uid || auth.token.admin === true"
30 | }
31 | }
32 | },
33 | "adminOwned": {
34 | "$objectType": {
35 | "$uid": {
36 | ".read": "auth.token.admin === true",
37 | ".write": "auth.token.admin === true"
38 | }
39 | }
40 | },
41 | "public": {
42 | "$objectType": {
43 | "$uid": {
44 | ".read": true,
45 | ".write": "auth.token.admin === true"
46 | }
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/starter-project/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "database": {
3 | "rules": "database.rules.json"
4 | },
5 | "firestore": {
6 | "rules": "firestore.rules",
7 | "indexes": "firestore.indexes.json"
8 | },
9 | "hosting": {
10 | "public": "public",
11 | "ignore": [
12 | "firebase.json",
13 | "**/.*",
14 | "**/node_modules/**"
15 | ],
16 | "rewrites": [
17 | {
18 | "source": "**",
19 | "destination": "/index.html"
20 | }
21 | ]
22 | },
23 | "storage": {
24 | "rules": "storage.rules"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/starter-project/firestore.indexes.json:
--------------------------------------------------------------------------------
1 | {
2 | // Example:
3 | //
4 | // "indexes": [
5 | // {
6 | // "collectionId": "widgets",
7 | // "fields": [
8 | // { "fieldPath": "foo", "mode": "ASCENDING" },
9 | // { "fieldPath": "bar", "mode": "DESCENDING" }
10 | // ]
11 | // }
12 | // ]
13 | "indexes": []
14 | }
--------------------------------------------------------------------------------
/starter-project/firestore.rules:
--------------------------------------------------------------------------------
1 | service cloud.firestore {
2 | match /databases/{database}/documents {
3 | match /{document=**} {
4 | allow read, write;
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/starter-project/functions/index.js:
--------------------------------------------------------------------------------
1 | const functions = require('firebase-functions');
2 |
3 | // // Create and Deploy Your First Cloud Functions
4 | // // https://firebase.google.com/docs/functions/write-firebase-functions
5 | //
6 | // exports.helloWorld = functions.https.onRequest((request, response) => {
7 | // response.send("Hello from Firebase!");
8 | // });
9 |
--------------------------------------------------------------------------------
/starter-project/functions/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "functions",
3 | "description": "Cloud Functions for Firebase",
4 | "scripts": {
5 | "serve": "firebase serve --only functions",
6 | "shell": "firebase experimental:functions:shell",
7 | "start": "npm run shell",
8 | "deploy": "firebase deploy --only functions",
9 | "logs": "firebase functions:log"
10 | },
11 | "dependencies": {
12 | "firebase-admin": "~5.8.1",
13 | "firebase-functions": "^0.8.1"
14 | },
15 | "private": true
16 | }
17 |
--------------------------------------------------------------------------------
/starter-project/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |