Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Example 4 - With FaunaDB Authentication #12

Draft
wants to merge 149 commits into
base: with-magic-link-auth
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
149 commits
Select commit Hold shift + click to select a range
396e7f1
Use refreshInterval to automatically refresh the UI once the user has…
Vadorequest Mar 8, 2021
82ce9d9
Improve logging
Vadorequest Mar 8, 2021
f6a29d9
Figured it out
Vadorequest Mar 9, 2021
258e390
Refactor "magic" > magicAdmin to avoid confusion
Vadorequest Mar 10, 2021
cbdb97b
Refactoring magicClient and make sure neither crash when called from …
Vadorequest Mar 10, 2021
73428b5
Improve TS defs + add doc for auth + increase security by calling "va…
Vadorequest Mar 10, 2021
31fbf9d
Add .env doc
Vadorequest Mar 10, 2021
a1ad950
Refactor libs (group them using folders)
Vadorequest Mar 10, 2021
cf2d0bf
Refactoring auth + more doc
Vadorequest Mar 10, 2021
5b2a6e8
Refactoring crypto
Vadorequest Mar 10, 2021
5cfd235
Rename TOKEN_SECRET > CRYPTO_TOKEN_SECRET
Vadorequest Mar 10, 2021
cd728a1
Upon login, create new User on faunadb if it doesn't exist or get use…
Vadorequest Mar 10, 2021
c1a196a
Doc
Vadorequest Mar 10, 2021
4f74151
More doc
Vadorequest Mar 10, 2021
dc51fa3
Update UserSession
Vadorequest Mar 10, 2021
7f9b738
Refactor streaming (WIP)
Vadorequest Mar 10, 2021
af827fc
wip
Vadorequest Mar 10, 2021
24aea38
Remove old faunadbClient.ts
Vadorequest Mar 10, 2021
6c4fa72
Rename "users" collection > Users
Vadorequest Mar 10, 2021
208190b
Fix streaming for guest users
Vadorequest Mar 10, 2021
557360f
Automatically create a new Canvas document for authenticated users or…
Vadorequest Mar 10, 2021
a409d29
Better TS defs + doc
Vadorequest Mar 10, 2021
fc6eec6
Remove variants (don't want to maintain old branches)
Vadorequest Mar 11, 2021
dfc54aa
Add public role
Vadorequest Mar 11, 2021
e9fdd3d
Add NEXT_PUBLIC_SHARED_FAUNABD_TOKEN env var
Vadorequest Mar 11, 2021
a895d70
Improve TS defs + doc for user API
Vadorequest Mar 11, 2021
8bef369
Add SelectAll utility
Vadorequest Mar 11, 2021
bbf2b35
Misc refactoring (no behavior change)
Vadorequest Mar 11, 2021
f50e974
Wait until user is fetched from APi before rendering, to avoid render…
Vadorequest Mar 11, 2021
ff5b238
Use hard refresh when logging in, simplifies the stream management
Vadorequest Mar 11, 2021
6e9775d
Improve logout (best practices) + doc
Vadorequest Mar 11, 2021
ee8c531
Add inspirations
Vadorequest Mar 11, 2021
52936ba
Optimize by ignore useless changes which tend to refresh the UI sever…
Vadorequest Mar 11, 2021
744ac48
Perform deep diff between before/after dataset to quickly understand …
Vadorequest Mar 11, 2021
d3be6a5
Remove dead code
Vadorequest Mar 11, 2021
ff83517
Add global styles + Animated3Dots.tsx component
Vadorequest Mar 11, 2021
c5f78ce
Improve UX when clicking on the "Send" btn (login/create account)
Vadorequest Mar 11, 2021
85c6da6
Fix TS error
Vadorequest Mar 11, 2021
34228f4
Better handling of streams, now close stream and open new one when us…
Vadorequest Mar 11, 2021
e70cef8
Store "ref" and "id" into the UserSession + use it to fetch the canva…
Vadorequest Mar 11, 2021
2b4dbaa
Update footer, add Magic link
Vadorequest Mar 11, 2021
3312cc4
modify UI styling (#10)
Dan-Hen Mar 11, 2021
261eb66
Misc doc login/form
Vadorequest Mar 11, 2021
aa2c05c
Improve VariableNameInput.tsx behavior (don't display "Saved" during …
Vadorequest Mar 12, 2021
345fa9e
Optimize streaming by storing a reference to the document (canvasDocR…
Vadorequest Mar 12, 2021
b562aae
Note about conflicts (unhandled)
Vadorequest Mar 12, 2021
9d4868b
Change Recoil dock shortcuts
Vadorequest Mar 12, 2021
723d175
Group logs to avoid spam
Vadorequest Mar 12, 2021
e228619
Add useTraceUpdate.ts hook
Vadorequest Mar 12, 2021
fc2b2ca
Don't update the database when clearing the canvas to avoid multiple …
Vadorequest Mar 12, 2021
00eaa67
Use useRenderingTrace to figure out what's causing a canvas re-render
Vadorequest Mar 12, 2021
be88556
Make it a bit more obvious what's the new/old value
Vadorequest Mar 12, 2021
1efa0b5
Optimize nodes/edges recoil subscription
Vadorequest Mar 12, 2021
29b7662
Remove logs when mutating nodes/edges (we have recoil dev tools for t…
Vadorequest Mar 12, 2021
5d06b60
Use collapsed group for logs that generate too much noise
Vadorequest Mar 12, 2021
0709ac4
Remove logs when mutating blockPickerMenuState (we have recoil dev to…
Vadorequest Mar 12, 2021
6b98763
Misc doc
Vadorequest Mar 12, 2021
8c5d5ea
Remove duplicated logs (noise)
Vadorequest Mar 12, 2021
e42a9be
Optimize misc
Vadorequest Mar 12, 2021
b37867b
Add usePreviousValue hook
Vadorequest Mar 13, 2021
4e4262d
Refactoring updateUserCanvas (now compares a diff between local datas…
Vadorequest Mar 13, 2021
29917c7
Refactor + optimize real-time by avoiding needless updates to the DB …
Vadorequest Mar 13, 2021
ffcfdd3
Doc real-time
Vadorequest Mar 13, 2021
7b9831e
Misc refacto
Vadorequest Mar 13, 2021
97a8a9a
Add TODO
Vadorequest Mar 13, 2021
01122e9
Overwrite local state with remote state when concurrent updates are d…
Vadorequest Mar 15, 2021
0c8d64c
Use 1px, not 0.5px because it's converted to 1px automatically
Vadorequest Mar 15, 2021
65a1f1a
Misc
Vadorequest Mar 15, 2021
e22caf7
Add dynHeights/dynWidths to all nodes
Vadorequest Mar 15, 2021
9b42f9a
Refactor defaultWidth/height > baseWidth/height + auto-update nodes w…
Vadorequest Mar 15, 2021
6333eb0
Use dynHeight to recalculate the height of the QuestionNode
Vadorequest Mar 15, 2021
dd3dde6
Remove unnamed const (was equal to the input's top+bottom margin), no…
Vadorequest Mar 15, 2021
e162254
Debounce onHeightChange through Textarea component
Vadorequest Mar 15, 2021
2b94511
Automatically debounce patchCurrentNode to increase app's perfs and p…
Vadorequest Mar 15, 2021
57054dc
Provide both patchCurrentNode and patchCurrentNodeImmediately to chil…
Vadorequest Mar 15, 2021
015bfd7
Refactoring naming to make it less confusing
Vadorequest Mar 15, 2021
43e63a1
Improve QuestionNode to recalculate its height when dynamic source va…
Vadorequest Mar 15, 2021
797bc0f
Refactor settings.canvas.nodes.defaultDebounceWaitFor (+ rename)
Vadorequest Mar 16, 2021
1988779
Rename onInformationTextHeightChange/onInformationTextInputValueChange
Vadorequest Mar 16, 2021
41e0500
Fix CSS
Vadorequest Mar 16, 2021
1641297
TS doc InformationNodeAdditionalData + add dynHeights
Vadorequest Mar 16, 2021
c2239bf
Display diff in useTraceUpdate
Vadorequest Mar 15, 2021
6f72790
Refactoring patchCurrentNode (using lodash deep merge)
Vadorequest Mar 15, 2021
f60671f
Simplify types + fix error
Vadorequest Mar 15, 2021
6c959cb
Fix issue when "diff" cannot be executed
Vadorequest Mar 15, 2021
f810323
Disable diff in useTraceUpdate
Vadorequest Mar 16, 2021
7300624
Use deep-object-diff instead of deep-diff lib, doesn't run an infinit…
Vadorequest Mar 16, 2021
60ed6df
Improve debugging with additional detailed diff
Vadorequest Mar 16, 2021
a1ae3fe
Don't log errors
Vadorequest Mar 16, 2021
9482f5b
Ignoring notifications from same editor
Vadorequest Mar 16, 2021
5e4ab52
Refactoring useUser > useUserSession + wait for session to be fetched…
Vadorequest Mar 16, 2021
b6b8d76
Misc TS doc
Vadorequest Mar 16, 2021
ce4bea8
Store lastUpdatedBySessionEphemeralId and lastUpdatedByUserName when …
Vadorequest Mar 16, 2021
a47f28d
Remove dead code (misc imports)
Vadorequest Mar 17, 2021
925faf5
Fix loading canvas for anonymous users
Vadorequest Mar 17, 2021
4cd35e7
Adapt from d511084b4470f30733e2f2397f7aee08aaa64f09
Vadorequest Mar 17, 2021
f20a30b
Fix height update
Vadorequest Mar 15, 2021
e1069d8
TS misc
Vadorequest Mar 15, 2021
d373bc3
Handle concurrent patches better (WIP)
Vadorequest Mar 15, 2021
ebc4bba
Few minor improvements, better but still not perfect
Vadorequest Mar 15, 2021
600275c
Add patchCurrentNodeConcurrently helper for nodes
Vadorequest Mar 15, 2021
fb037c4
Adapt InformationNode
Vadorequest Mar 15, 2021
ba2ce22
Dynamically auto-recalculate actual width/height when base changes (i…
Vadorequest Mar 15, 2021
25e8c2f
Always add borderWidth to nodes to avoid content jump when selected
Vadorequest Mar 17, 2021
0783073
Added a changes queue to apply all changes at once as one consolidate…
Vadorequest Mar 17, 2021
a907b76
Fix mutations
Vadorequest Mar 17, 2021
abe30f8
Use state and auto-refresh UI when state changes
Vadorequest Mar 17, 2021
b3da286
Refactor, move mutations handling to a dedicated file + doc
Vadorequest Mar 17, 2021
d247de7
Fix calculation of dynamic height for QuestionNode
Vadorequest Mar 17, 2021
8265ba4
Add more FQL config (not 100% working)
Vadorequest Mar 17, 2021
574a8c9
Renaming (misc)
Vadorequest Mar 17, 2021
47f63c4
Add useDebouncedEffect to avoid starting too many streams at once whe…
Vadorequest Mar 17, 2021
5ff226c
Improve streaming error handling
Vadorequest Mar 17, 2021
7709505
Fix Public:read role
Vadorequest Mar 17, 2021
14513e8
Fix Public role configuration
Vadorequest Mar 17, 2021
652af82
Fix Editor role create/write permissions + add doc
Vadorequest Mar 17, 2021
07bf499
Update documentation
Vadorequest Mar 17, 2021
f4d54bc
Add roadmap
Vadorequest Mar 17, 2021
342fc48
Add FaunaDB IaC
Vadorequest Mar 17, 2021
2b72b09
Contributing
Vadorequest Mar 17, 2021
943cd4a
Renaming "patch" > "changes"
Vadorequest Mar 17, 2021
6a70b08
Improve debugging experience by cloning all object printed to the con…
Vadorequest Mar 17, 2021
4064fb0
Implement "add" on queue
Vadorequest Mar 17, 2021
c918f3c
Implement "delete" on queue
Vadorequest Mar 17, 2021
b0a5f60
Implement "add/patch/delete" on queue (edge)
Vadorequest Mar 17, 2021
c0bda6b
Replace more setEdges calls by queue
Vadorequest Mar 17, 2021
7f37e04
Misc const instead of function
Vadorequest Mar 17, 2021
0d2e037
Refactor addNodeAndEdgeThroughPorts to return only what's changed ins…
Vadorequest Mar 17, 2021
bb86863
Refactoring, move business logic in addNodeAndEdgeThroughPorts
Vadorequest Mar 17, 2021
fd4d912
Refactoring upsertNodeThroughPorts to use batch mutations
Vadorequest Mar 17, 2021
44503e6
Rename addCanvasDatasetMutation > queueCanvasDatasetMutation
Vadorequest Mar 17, 2021
44d1e22
Misc doc
Vadorequest Mar 17, 2021
2bd3c6f
Add textarea width settings
Vadorequest Mar 17, 2021
38fb044
Add todo
Vadorequest Mar 17, 2021
621e4b3
Remove deep-diff lib, use deep-object-diff instead
Vadorequest Mar 17, 2021
919204f
Ignore textarea border width because it forces recalculations
Vadorequest Mar 17, 2021
3593f0b
Fix onInformationTextHeightChange & cie
Vadorequest Mar 17, 2021
15f6950
Add deploy:fake
Vadorequest Mar 19, 2021
ffd1274
Change CanvasByOwnerIndex shape (was an array of arrays, now it's sim…
Vadorequest Mar 19, 2021
9533248
Fix membership definition for roles
Vadorequest Mar 19, 2021
ad5e763
Fix roles GQL (missing "Query" wrapper)
Vadorequest Mar 19, 2021
c57cc4e
Reformat misc
Vadorequest Mar 19, 2021
3b27559
Fix create shared canvas
Vadorequest Mar 19, 2021
9048baf
Add doc getUserByEmail no throw
Vadorequest Mar 19, 2021
5e025de
Throw when obtainFaunaDBToken catch an exception
Vadorequest Mar 19, 2021
d3026ed
Split code in login to make it easier to debug
Vadorequest Mar 19, 2021
a2d3f83
Fix lastUpdatedByUserName when anonymous
Vadorequest Mar 19, 2021
baa5de3
Merge branch 'with-magic-link-auth' into with-faunadb-auth
Vadorequest Mar 21, 2021
d86cc29
Rename indexes
Vadorequest Mar 29, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .env.local.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
# Go to https://dashboard.magic.link/ > API Keys > Test "Publishable key"
NEXT_PUBLIC_MAGIC_PUBLISHABLE_KEY=

# FaunaDB token to generate from FaunaDB, based on the "Public" role.
# Go to https://dashboard.fauna.com/ > Select DB > Shell > Run fql/setup.js (if not done already)
# Go to https://dashboard.fauna.com/ > Select DB > Security > New Key > Role: Public | Name: PUBLIC_SHARED_FAUNABD_TOKEN
NEXT_PUBLIC_SHARED_FAUNABD_TOKEN=

# Magic Link provides a "secret key" which must only be used from the server.
# Go to https://dashboard.magic.link/ > API Keys > Test "Secret key"
MAGIC_SECRET_KEY=
Expand All @@ -14,3 +19,8 @@ MAGIC_SECRET_KEY=
# Changing this secret will invalidate all existing user sessions. (they'll have to log in again)
# You can generate a string using https://passwordsgenerator.net/ (recommended 32 chars, no special chars)
CRYPTO_TOKEN_SECRET=

# Server secret key for FaunaDB.
# Used to perform actions from the server-side (creating users, etc.)
# Go to https://dashboard.fauna.com/ > Select DB > Security > New Key > Role: Server | Name: FAUNADB_SERVER_SECRET_KEY
FAUNADB_SERVER_SECRET_KEY=
205 changes: 170 additions & 35 deletions README.md

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions fql/examples.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Create(
Collection('Canvas'),
{
data: {
owner: Ref(Collection("Users"), "292674252603130373"),
nodes: [],
edges: [],
}
},
)

Lambda("ref", Equals(
CurrentIdentity(),
Select(["data", "owner"], Get(Var("ref")))
))
165 changes: 165 additions & 0 deletions fql/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// ---------------------- Step 1: Create a "Users" collection ----------------------
CreateCollection({ name: 'Users' });

// ---------------------- Step 2: Create "Canvas" collection ----------------------
CreateCollection({ name: 'Canvas' });

// ---------------------- Step 3: Create indexes ----------------------
// Index to filter users by email
// Necessary for authentication, to find the user document based on their email
CreateIndex({
name: 'usersByEmail',
source: Collection('Users'),
terms: [
{ field: ['data', 'email'] },
],
unique: true,
});

// Index to filter canvas by owner
// Necessary for real-time subscription, to retrieve the canvas of the current user
CreateIndex({
name: 'canvasByOwner',
source: Collection('Canvas'),
// Needs permission to read the Users, because "owner" is specified in the "terms" and is a Ref to the "Users" collection
permissions: {
read: Collection('Users'),
},
// Allow to filter by owner ("Users")
terms: [
{ field: ['data', 'owner'] },
],
// Index contains the Canvas ref (that's the default behavior and could be omitted)
values: [
{ field: ['ref'] },
],
});

// ---------------------- Step 4: Create roles ----------------------

// The "Editor" role is assigned to all authenticated users
// It is automatically assigned when a user is authenticated, because it defines "membership" to the Users collection
// It is secure because the token is generated upon login on the server-side and stored in a "httpOnly" cookie that can only be read/written on the server-side
// The token is specific to the user and is used on the frontend
// The token only allows the user to read/write documents that belongs to him
CreateRole({
name: 'Editor',
// All users should be editors (will apply to authenticated users only).
membership: [
{
resource: Collection('Users'),
},
],
privileges: [
{
// Editors need read access to the canvasByOwner index to find their own canvas
resource: Index('canvasByOwner'),
actions: {
read: true,
},
},
{
resource: Collection('Canvas'),
actions: {
// Editors should be able to read (+ history) of Canvas documents that belongs to them.
read: Query(
Lambda('ref', Equals(
CurrentIdentity(),
Select(['data', 'owner'], Get(Var('ref'))),
)),
),
history_read: Query(
Lambda('ref', Equals(
CurrentIdentity(),
Select(['data', 'owner'], Get(Var('ref'))),
)),
),
// Editors should be able to edit only Canvas documents that belongs to them
write: Query(
Lambda(
['oldData', 'newData', 'ref'],
And(
// The owner in the current data (before writing them) must be the current user
Equals(
CurrentIdentity(),
Select(['data', 'owner'], Var('oldData')),
),
// The owner must not change
Equals(
Select(['data', 'owner'], Var('oldData')),
Select(['data', 'owner'], Var('newData')),
),
),
),
),
// Editors should be able to create only Canvas documents that belongs to them
create: Query(
Lambda('values', Equals(
CurrentIdentity(),
Select(['data', 'owner'], Var('values'))),
),
),
},
},
],
});

// The "Public" role is assigned to anyone who isn't authenticated
// It doesn't use "membership" (unlike "Editor" role) but a token created manually that doesn't expire
// It is secure because the token only grant access to the special document of id "1", which is shared amongst all guests
// Guests can only read/write this particular document and not any other
CreateRole({
name: 'Public',
// The public role is meant to be used to generate a token which allows anyone (unauthenticated users) to update the canvas
membership: [],
privileges: [
{
resource: Collection('Canvas'),
actions: {
// Guests should only be allowed to read the Canvas of id "1"
read: Query(
Lambda('ref',
Equals(
'1',
Select(['id'], Var('ref'),
),
),
),
),
// Guests should only be allowed to update the Canvas of id "1"
write: Query(
Lambda(
['oldData', 'newData', 'ref'],
Equals(
'1',
Select(['id'], Var('ref')),
),
),
),
// Guests should only be allowed to create the Canvas of id "1"
create: Query(
Lambda('values',
Equals(
'1',
Select(['ref', 'id'], Var('values')),
),
),
),
// Creating a record with a custom ID requires history_write privilege
// See https://fauna-community.slack.com/archives/CAKNYCHCM/p1615413941454700
history_write: Query(
Lambda(
['ref', 'ts', 'action', 'data'],
Equals(
'1',
Select(['id'], Var('ref')),
),
),
),
},
},
],
});

// Create the shared Canvas record
Create(Ref(Collection('Canvas'), '1'));
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"build": "next build",
"start": "next dev --port 8890",
"type-check": "tsc",
"link:reaflow": "yarn link reaflow && yarn link react && yarn link react-dom"
"link:reaflow": "yarn link reaflow && yarn link react && yarn link react-dom",
"deploy:fake": "git commit --allow-empty -m \"Fake empty commit (force CI trigger)\""
},
"dependencies": {
"@chakra-ui/icons": "1.0.4",
Expand All @@ -20,6 +21,7 @@
"@hapi/iron": "6.0.0",
"@magic-sdk/admin": "1.3.0",
"@types/lodash.clonedeep": "4.5.6",
"@types/lodash.isempty": "4.4.6",
"@types/lodash.merge": "4.6.6",
"@types/lodash.now": "4.0.6",
"@types/lodash.size": "4.2.6",
Expand All @@ -28,6 +30,7 @@
"animate.css": "4.1.1",
"classnames": "2.2.6",
"cookie": "0.4.1",
"deep-object-diff": "1.1.0",
"faunadb": "4.1.1",
"faunadb-fql-lib": "0.13.0",
"framer-motion": "3.2.1",
Expand All @@ -36,7 +39,9 @@
"lodash.debounce": "4.0.8",
"lodash.filter": "4.6.0",
"lodash.includes": "4.3.0",
"lodash.isempty": "4.4.0",
"lodash.isequal": "4.5.0",
"lodash.isobjectlike": "4.0.0",
"lodash.merge": "4.6.2",
"lodash.now": "4.0.2",
"lodash.remove": "4.7.0",
Expand All @@ -57,6 +62,7 @@
"recoil-devtools-log-monitor": "^0.2.7",
"recoil-devtools-logger": "^0.1.5",
"swr": "0.4.2",
"use-debounce": "6.0.0",
"uuid": "8.3.2"
},
"devDependencies": {
Expand All @@ -68,6 +74,7 @@
"@types/lodash.filter": "4.6.6",
"@types/lodash.includes": "4.3.6",
"@types/lodash.isequal": "4.5.5",
"@types/lodash.isobjectlike": "4.0.6",
"@types/lodash.remove": "4.7.6",
"@types/lodash.some": "4.6.6",
"@types/lodash.sortby": "4.7.6",
Expand Down
59 changes: 59 additions & 0 deletions src/components/Animated3Dots.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React from 'react';

export type Props = {
/**
* Color of the dots.
*
* @default white
*/
fill?: string;
};

/**
* An animated composant featuring 3 animated dots "...".
*
* Each dot is animated separately, in alternation.
* Requires animate.css library.
*
* @see https://animate.style
*/
const Animated3Dots = (props: Props): JSX.Element => {
return (
<svg
id="AnimatedBubble_svg__Calque_1"
x={0}
y={0}
viewBox="0 0 19 5"
width="20px"
style={{
overflow: 'visible',
paddingTop: 5,
marginLeft: 5,
}}
xmlSpace="preserve"
fill={'white'}
{...props}
>
<circle
className="animate__animated animate__fadeIn animate__infinite delay-200ms"
cx={2.783}
cy={2.796}
r={2.153}
/>
<circle
className="animate__animated animate__fadeIn animate__infinite delay-400ms"
cx={9.576}
cy={2.796}
r={2.153}
/>
<circle
className="animate__animated animate__fadeIn animate__infinite delay-600ms"
cx={16.369}
cy={2.796}
r={2.153}
/>
</svg>
);
};

export default Animated3Dots;
Loading