diff --git a/docs/API/components/email-verification.md b/docs/API/components/email-verification.md index 205a8845..5f1d16c8 100644 --- a/docs/API/components/email-verification.md +++ b/docs/API/components/email-verification.md @@ -16,6 +16,56 @@ Fliplet.Verification.Email.get() The method returns a promise that resolves when the email verification component is rendered. +## What happens after successful verification + +When a user successfully verifies their email or SMS code, Fliplet authenticates them with a `dataSource` passport session. This means the user stays logged in after verification and can be recognized by app security rules, data source security rules, and other APIs that rely on the current session. + +Use `Fliplet.User.getCachedSession()` to read the authenticated entry: + +```js +Fliplet.User.getCachedSession().then(function (session) { + var entry = _.get(session, 'entries.dataSource'); + + if (!entry) { + return; // user is not logged in with a dataSource passport + } + + // Public session shape + // entry.id - the authenticated data source entry ID + // entry.dataSourceId - the authentication data source ID + // entry.data - the authenticated row's flat column values + console.log(entry); +}); +``` + +A typical public session shape looks like this: + +```js +{ + entries: { + dataSource: { + id: 123, + dataSourceId: 456, + data: { + ID: 123, + Email: 'john@example.org', + Department: 'Sales', + Role: 'Member' + } + } + } +} +``` + +

Important: Email Verification does not only validate identity for the current screen. It creates a reusable logged-in session using the dataSource passport.

+ +This authenticated state is compatible with Data Source security rules: + +- `allow: "loggedIn"` recognizes the user as authenticated +- {% raw %}`{{user.[Email]}}`, `{{user.[Department]}}`, `{{user.[Role]}}`, and `{{user.[ID]}}`{% endraw %} resolve against the authenticated row + +See the Session JS APIs and Securing your Data Sources docs for the full session and security model. + ## Instance properties The `verification` instance variable above makes available the following instance properties. @@ -61,7 +111,7 @@ Fliplet.Verification.Email.get().then(function (verification) { ### Run code after email/SMS verification -The `onUserVerified` hook is fired after a user successfully validates their verification code (email or SMS). Use this to run custom logic such as redirecting users, fetching additional data, or setting encryption keys. +The `onUserVerified` hook is fired after a user successfully validates their verification code (email or SMS) and the `dataSource` passport session has been established. Use this to run custom logic such as redirecting users, fetching additional data, or setting encryption keys. ```js Fliplet.Hooks.on('onUserVerified', function (data) { diff --git a/docs/API/components/login.md b/docs/API/components/login.md index 76742651..d900f28c 100644 --- a/docs/API/components/login.md +++ b/docs/API/components/login.md @@ -90,7 +90,7 @@ Fliplet.Hooks.on('sessionValidate', function (data) { |----------|------| | User logs in with username + password | `login` | | User logs in via SAML2 SSO | `login` | -| User verifies via email or SMS code | `onUserVerified` (see [Email Verification](email-verification.html)) | +| User verifies via email or SMS code | `onUserVerified` (see [Email Verification](email-verification.html) for the hook, session shape, and security-rule compatibility) | | Returning user's session is re-validated | `sessionValidate` |

Tip: If you need to run the same code regardless of how the user authenticated, register handlers for both login and onUserVerified.

diff --git a/docs/API/fliplet-session.md b/docs/API/fliplet-session.md index 049d3b72..b4318039 100644 --- a/docs/API/fliplet-session.md +++ b/docs/API/fliplet-session.md @@ -70,16 +70,17 @@ Fliplet.Session.logout().then(function onSessionDestroyed() { ## Read details about connected accounts -If your app contains a login component (either DataSource, SAML2 or Fliplet) you can use the session to check whether the user is logged in and in and some of the connected account(s) details: +If your app contains an authentication component (for example Data Source Login, Email Verification, SMS verification, SAML2, or Fliplet login) you can use the session to check whether the user is logged in and read the connected account details: ```js Fliplet.User.getCachedSession().then(function(session) { if (session && session.entries) { // the user is logged in; - // check if the user is connected to a dataSource login + // check if the user is connected to a dataSource passport if (session.entries.dataSource) { - // user is logged in against a Fliplet dataSource + // user is logged in through a dataSource-based flow + // such as Data Source Login, Email Verification, or SMS verification } // check if the user is connected to a SAML2 login @@ -101,6 +102,8 @@ Data for the connected account(s) can also be read and used as necessary: ### Example for dataSource login +This public session shape is used by any authentication flow that logs the user in with the `dataSource` passport, including Email Verification and SMS verification. + ```js Fliplet.User.getCachedSession().then(function (session) { var user = _.get(session, 'entries.dataSource.data'); @@ -114,6 +117,8 @@ Fliplet.User.getCachedSession().then(function (session) { }); ``` +

Note: For client-side app code, use session.entries as the public session interface. Do not rely on internal server/session structures.

+ ### Example for SAML2 ```js diff --git a/docs/App-security.md b/docs/App-security.md index fd55712e..56c24e54 100644 --- a/docs/App-security.md +++ b/docs/App-security.md @@ -18,7 +18,7 @@ If you need more control on your security rules, you can also write your custom - `session` (Object) the user's session, when available. Contains the same attributes found in the `v1/session` endpoint - `ipRangeCheck` (Function) please check the next section on IP address whitelisting/blacklisting for usage -Here follows an example that protects all screens (aside from the `loginScreen`) from being accessed unless the user is logged in against a **dataSource** and the column `foo` of his user has value `bar`. +Here follows an example that protects all screens (aside from the `loginScreen`) from being accessed unless the user is logged in with a **dataSource** session, such as Data Source Login, Email Verification, or SMS verification, and the column `foo` of the authenticated row has value `bar`. When those conditions are not met, an `error` is raised and the user is redirected (see `navigate`) to the `loginScreen`: @@ -147,4 +147,4 @@ The two options can also be used together: ![Data Sources](https://user-images.githubusercontent.com/574210/197834498-dceeecdc-f5ac-4315-b629-dd8434c4a5b0.png) ---- \ No newline at end of file +--- diff --git a/docs/Data-source-security.md b/docs/Data-source-security.md index f7c2d4a9..d0d98eef 100644 --- a/docs/Data-source-security.md +++ b/docs/Data-source-security.md @@ -62,6 +62,10 @@ The `allow` property supports four modes: { "allow": "loggedIn" } ``` +`allow: "loggedIn"` checks whether the current request is authenticated. This includes users authenticated through the `dataSource` passport, such as Data Source Login, Email Verification, and SMS verification. + +

Important: allow: "loggedIn" only checks whether the user is authenticated. It does not limit which records they can access. Use require with {% raw %}{{user[...]}}{% endraw %} to scope access to the authenticated user's data.

+ **Specific users** (filtered by session data). `allow.user` checks the logged-in user's **identity** — use it with literal values to control *who* can access. To control *which records* they can access, use `require` instead (see [Data requirements](#data-requirements-and-query-validation)): ```json @@ -466,6 +470,8 @@ Condition values can reference the logged-in user's session data using Handlebar - {% raw %}`{{user.[Role]}}`{% endraw %} — the user's role - {% raw %}`{{user.[ID]}}`{% endraw %} — the user's data source entry ID +For Data Source Login, Email Verification, and SMS verification, these values come from the authenticated row in the authentication data source. For example, {% raw %}`{{user.[Email]}}`{% endraw %} resolves to the verified email address, and {% raw %}`{{user.[ID]}}`{% endraw %} resolves to the authenticated entry ID. + ### Require syntax **Required fields** (string) — the query must include these columns: @@ -501,6 +507,52 @@ You can mix both formats in the same array: For full examples of `require` in practice — including sample data, succeed queries, and fail queries — see [role-based access with protected fields](#example-role-based-access-with-protected-fields) and [department-scoped access](#example-department-scoped-access) above. +### Example: email verification with secured data access + +The following rule allows authenticated users to read only the records that match their verified email address: + +{% raw %} +```json +{ + "type": ["select"], + "allow": "loggedIn", + "require": [ + { "Email": { "equals": "{{user.[Email]}}" } } + ] +} +``` +{% endraw %} + +Client-side code must include a matching `where` clause: + +```js +Fliplet.User.getCachedSession().then(function (session) { + var user = _.get(session, 'entries.dataSource.data'); + + if (!user) { + return; + } + + return Fliplet.DataSources.connectByName('Orders').then(function (connection) { + return connection.find({ + where: { Email: user.Email } + }); + }); +}); +``` + +This succeeds because the user is authenticated and the query satisfies the rule's `require`. + +The following query does not satisfy the rule: + +```js +Fliplet.DataSources.connectByName('Orders').then(function (connection) { + return connection.find(); +}); +``` + +For reads, an unmet `require` means the rule is skipped. Access is only granted if a later rule allows it. + ## Custom security rules For advanced logic beyond what the standard rule properties support, you can write custom JavaScript security rules. @@ -682,4 +734,4 @@ Both methods accept the following properties: - `limit` (Number, defaults to `100`) - `offset` (Number, defaults to `0`) -

Use DataSources('Name') (data source name) instead of DataSources(123) (ID) in custom scripts. Data source names are preserved during app clone, so name-based lookups continue to work without manual remapping.

\ No newline at end of file +

Use DataSources('Name') (data source name) instead of DataSources(123) (ID) in custom scripts. Data source names are preserved during app clone, so name-based lookups continue to work without manual remapping.