Security
Authentication
this article provides an introduction to ditto's methods for authentication, as well as step‑by‑step instructions on how to use the online playground authentication mode to initialize ditto and start prototyping ditto's security relies on digital identities for peer to peer authentication and access control permissions and privileges each instance of ditto running in your app must prove its identity to other peers before it is allowed to sync the following table provides an overview of authentication in ditto method use case onlineplayground ¹ you're ready to explore and test realtime sync capabilities without the commitment using either a demo app or your own development app onlinewithauthentication you've integrated ditto, integrated a third‑party identity service provider, and are now ready to go into production ¹do not use the online playground identity for production level apps the online playground is intended for testing and experimentation purposes only deploying playground certificates to a live environment could lead to vulnerabilities and security risks onlineplayground identity before you can sync offline, you must first connect to the internet to retrieve your playground certificate using ditto's online playground identity, you can sync with other peers that share the same app id and read and write to collections without the hassle of needing to set up your own authentication each instance of ditto running in your app is assigned a random universally unique identifier (uuid), which is the string of alphanumeric characters that identify your app across ditto, as well as all other systems and environments the online playground is an authentication mode that allows you to explore platform features and functionality without the hassle of setting up your own authentication mechanism before you can sync offline, you must connect to the internet to obtain your playground certificate from the big peer unless you have a specialized use case, such as a government app, you must connect to the internet at least once before you can sync offline with other peers the playground certificate is an x 509 formatted certificate generated automatically by the big peer when a network connection is available once you've obtained your playground certificate from the big peer, you can go offline and sync with unrestricted platform access for a maximum of one week — reading and writing to all collections and establishing connections with all peers sharing the same app id after one week, however, your playground certificate expires and you lose access to the platform, requiring you to reconnect to the internet so the big peer can automatically generate a new playground certificate initializing ditto and going offline to explore ditto without the commitment, authenticate using the playground identity if you want to use ditto in an air gapped environment that is entirely offline or on‑premise, contact ditto customer engineering (see docid\ hwtjr8xgjry4a7iqleghz ) from the portal, create an app and get your access credentials for instructions, see docid\ up5t7ykh9nwxb6qngtp4x your access credentials consist of an app id and playground token the app id is a unique identifier that ditto uses to recognize your app the big peer uses the app id to identify your app before issuing your playground certificate peers that share the same app id will automatically form a mesh network, using any transports available on their respective devices the playground token is a passphrase that small peers use to authenticate your digital identity with the big peer once authenticated, the big peer issues a unique json web token (jwt) to establish and ensure persistence of connection between small peers and the big peer as long as the playground token remains active, your connection persists in the top most scope of the codebase of your app, set up and initialize ditto and configure your app to use the online playground for testing dittologger minimumloglevel = debug let ditto = ditto(identity onlineplayground( appid "replace me with your app id", token "replace me with your playground token" )) do { try ditto startsync() } catch (let err) { print(err localizeddescription) }try { val androiddependencies = defaultandroiddittodependencies(context) val identity = dittoidentity onlineplayground( androiddependencies, appid = "replace me with your app id", token = "replace me with your playground token" ) dittologger minimumloglevel = dittologlevel debug ditto = ditto(androiddependencies, identity) ditto startsync() } catch (e dittoerror) { log e("ditto error", e message!!) }import { init, ditto, logger } from "@dittolive/ditto" logger minimumloglevel = 'debug' const identity identity = { type 'onlineplayground', appid 'replace me with your app id', token 'replace me with your playground token' } const path = ' /your ditto application data path' const ditto = new ditto(identity, path) ditto startsync()import { init, ditto, logger } from "@dittolive/ditto" logger minimumloglevel = 'debug' const identity identity = { type 'onlineplayground', appid 'replace me with your app id', token 'replace me with your playground token' } const path = ' /your ditto application data path' const ditto = new ditto(identity, path) ditto startsync()dittodependencies androiddependencies = new defaultandroiddittodependencies(this context); dittologger setminimumloglevel(dittologlevel debug); dittoidentity identity = new dittoidentity onlineplayground(androiddependencies, "replace me with your app id", "your playground token here"); ditto ditto = new ditto(androiddependencies, identity); try { ditto startsync(); } catch(dittoerror e) { //handle error }try { dittologger setminimumloglevel(dittologlevel debug); var ditto = new ditto(dittoidentity onlineplayground("replace me with your app id", "replace me with your playground token", true), path); ditto startsync(); } catch (dittoexception ex) { console writeline($"ditto error {ex message}"); }auto identity = identity onlineplayground("replace me with your app id", "replace me with your playground token", true); try { ditto ditto = ditto(identity, dir); ditto set minimum log level(loglevel debug); ditto start sync(); } catch (const dittoerror \&err) { }let ditto = ditto builder() // creates a `ditto data` folder in the directory containing the executing process with root(arc new(persistentroot from current exe()?)) with identity(|ditto root| { // provided as an env var, may also be provided as hardcoded string let app id = appid from env("replace me with your app id")?; let shared token = std env var("replace me with a shared token") unwrap(); let enable cloud sync = true; let custom auth url = none; onlineplayground new( ditto root, app id, shared token, enable cloud sync, custom auth url, ) })? with minimum log level(loglevel debug) build()?; ditto start sync()?;# not supported by http api onlinewithauthentication identity ditto does not come with an identity provider using "online with authentication" requires that you have your own identity provider already set up each app can use multiple identity providers the "online with authentication" identity type is geared towards apps that will be deployed in real world settings "online with authentication" identity types are for apps that need to integrate with existing permissions for apps that need to integrate with existing authentication systems ditto does not provide identity access services therefore, in order to use onlinewithauthentication for authentication in your production‑ready app, you must first integrate a third party identity provider for login authentication and authorization for more information, see platform manual > docid\ h1jmwiwxxn4m zsylzfdx api https //deploy preview 643 shiny croquembouche 15669a netlify app/ios/common/security/authentication#api to see fully working examples, see the https //github com/getditto/examples permission creating your client create the ditto client with the onlinewithauthentication identity this identity requires an authentication handler authhandler you must refresh the auth token when it expires you can do that by implementing authenticationexpiringsoon if you do not implement this, then sync will stop when the token expires struct authdelegate dittoauthenticationdelegate { func authenticationrequired(authenticator dittoauthenticator) { print("login request ") } func authenticationexpiringsoon(authenticator dittoauthenticator, secondsremaining int64) { print("auth token expiring in \\(secondsremaining)") } } let identity = dittoidentity onlinewithauthentication( appid "replace me with your app id", authenticationdelegate authdelegate() ) let ditto = ditto(identity identity) try! ditto startsync()class authcallback dittoauthenticationcallback { override fun authenticationrequired(authenticator dittoauthenticator) { println("login request ") } override fun authenticationexpiringsoon( authenticator dittoauthenticator, secondsremaining long ) { println("auth token expiring in $secondsremaining seconds") } } val androiddependencies = androiddittodependencies(context) val identity = dittoidentity onlinewithauthentication( androiddependencies, "replace me with your app id", authcallback() ) val ditto = ditto(androiddependencies, identity) try { ditto startsync() } catch(e dittoerror) { log e("ditto error", e message!!) }class authdelegate idittoauthenticationdelegate { public async void authenticationrequired(dittoauthenticator authenticator) { system console writeline($"login request"); } public async void authenticationexpiringsoon(dittoauthenticator authenticator, long secondsremaining) { system console writeline($"auth token expiring in {secondsremaining} seconds"); } } var identity = dittoidentity onlinewithauthentication( "replace me with your app id", new authdelegate()); try { var ditto = new ditto(identity); ditto startsync(); } catch (dittoexception ex) { system console writeline($"ditto error {ex message}"); } login login takes two parameters the first is token the token can be any string value most auth services use a https //jwt io/ , but you can send any token you want from the client for example, during testing you may want to create a secret code for development use this string will be sent in a post request to the http route the second parameter is provider , this provider must be a string value matching the name of one of your configured authentication webhooks if you have multiple authentication webhooks, you can select which webhook your application should use by changing the provider ditto auth login(accesstoken, provider "my auth") { clientinfo, err in print("login request completed error? \\(err)") }ditto auth login(accesstoken, "my auth", err > { system out println("login request completed error? " + err tostring()); });var res = await authenticator login(accesstoken, "my auth"); system console writeline($"login request completed error? {res}"); when should you call ditto auth login ? https //deploy preview 643 shiny croquembouche 15669a netlify app/ios/common/security/authentication#when should you call dittoauthlogin if you have already implemented the callbacks mentioned earlier, you don't need to take further action ditto will automatically attempt to refresh whenever possible however, if you know specific times when the app will be online, you may choose to call this function manually for an example implementation of authentication for android, see the ditto open source demo chat app's " https //github com/getditto/demoapp chat/tree/authentication " branch in the getditto github repository logout logout will stop sync, shut down all replication sessions, and remove any cached authentication credentials note that this does not remove any data from the store if you wish to delete data from the store then use the optional cleanupfn parameter to perform any required cleanup the cleanupfn is an optional function that will be called with the relevant ditto instance as the sole argument that allows you to perform any required cleanup of the store as part of the logout process ditto auth logout(cleanup { ditto in ditto store collection("cars") findall() evict() })ditto auth logout((ditto ditto) => { ditto store collection("cars") findall() evict(); }); tutorial this section will require knowledge of writing server side http endpoints and handlers the server side sample code is written in javascript (nodejs with an https //expressjs com/ like api), however you can use any framework or language of your choosing we will use auth0 in this tutorial but you can use any third party identity provider each app can use multiple identity providers in this tutorial, you'll build a simple application so users can log in with a a third party provider using https //auth0 com/ we assume that you have already completed the auth0 tutorial on their documentation before starting this tutorial for the full application code in javascript and swift, see the https //github com/getditto/examples permission configure ditto to use an "online with authentication" identity, go to your app in the https //portal ditto live/ and find the authentication mode & webhook settings section ensure that "with authentication" is selected like so below, a section called authentication webhooks will be editable once your authentication webhook endpoint(s) is deployed and ready, you can register it in the portal add a name and url provide a unique the url parameter is the fully qualified url of the webhook that you deploy yourself please include https // at the beginning of your url you can use our example webhook to just get started this webhook is a server deployed on a third party server and is just for testing this server will always authorize all clients with full read and write permissions you can use this url to test your application however, you should not use this url in production this replit simply authenticates everyone for 7 days of offline usage https //alloweveryonewebhook tester28 repl co/auth once configured, you should see a webhook that looks like this in your portal app settings configure auth0 https //deploy preview 643 shiny croquembouche 15669a netlify app/ios/common/security/authentication#configure auth0 the second step is to configure auth0 follow these steps create a new auth0 application configure the allowed callbacks and origins for your application make sure to add the callback url for your application configure the allowed grant types for your application for this tutorial, we will use the "authorization code" grant type create a new api in auth0 this will represent the api that your application will access next, you need to configure ditto follow the steps that were outlined earlier in this article now that you have configured auth0 and ditto, you can start integrating them into your application if you already have an auth0 account https //deploy preview 643 shiny croquembouche 15669a netlify app/ios/common/security/authentication#if you already have an auth0 account log in, skip the next section, and proceed to the part titled register your app with auth0 if you don't have an auth0 account yet https //deploy preview 643 shiny croquembouche 15669a netlify app/ios/common/security/authentication#if you dont have an auth0 account yet you can https //auth0 com/signup it's free register your app with auth0 in the menu on the left side of the auth0 dashboard, click on applications this will expand the applications menu select the first item in that menu, which also has the name applications you will now be on the applications page it lists all the applications that you have registered so that auth0 can handle their login/logout processes create a new registration for your app do this by clicking the create application button near the top right of the page you can follow the prompts and instructions on the auth0 site for more details from the auth0 portal you will need to retrieve the following information for your android app domain client id you can store these as string resources in your app on the auth0 portal, you will need to build your callback url and logout url again, see the auth0 website for details on how to do this references https //auth0 com/blog/get started with android authentication using kotlin part 1/ https //manage auth0 com/dashboard/us/dev voiik8orqv6m487g/onboarding integrating auth0 with ditto assuming you have a login button in the html \<button onclick={login}>login\</button> we attach a login function to the button import createauth0client from '@auth0/auth0 spa js'; // or for react import { useauth0 } from '@auth0/auth0 react'; // configure your auth0 client async function login () { await auth0 loginwithredirect({ redirect uri window\ location origin }); startditto() } we can then create a startditto function that gets the access token and starts a new ditto instance, and passes the token to your server route you created in the previous section the provider name given to the ditto client must match a provider name in the portal (e g , replit auth ) if you have multiple providers, ensure you specify the provider name that you want your app to use import createauth0client from '@auth0/auth0 spa js'; // or for react import { useauth0 } from '@auth0/auth0 react'; import { init, ditto } from "@dittolive/ditto" // configure your auth0 client let ditto (async () => { await init() // you need to call this at least once before using any of the ditto api function startditto () { let token = await auth0 getaccesstokensilently(); const authhandler = { authenticationrequired async function(authenticator) { await authenticator login(token, "replit auth"); console log("login request completed "); }, authenticationexpiringsoon function(authenticator, secondsremaining) { console log(`auth token expiring in ${secondsremaining} seconds`) await authenticator login(token, "replit auth"); console log("login request completed "); } } const identity = { type 'onlinewithauthentication', appid 'replace me with your app id', authhandler } ditto = new ditto(identity, '/persistence/file/path') ditto startsync() } async function login () { await auth0 loginwithredirect({ redirect uri window\ location origin }); startditto() } })() to demonstrate that this ditto client has been authenticated, let's display the number of cars in the collection, and a button to add one item to it jsx \<div> \<h1>cars {numberofcars}\</h1> \<button onclick={additem}>+1\</button> \</div> once we start the ditto instance, we can create a livequery and create a button that adds items to a collection let subscription = ditto store collection('cars') find("state == 'for sale'") subscribe() let livequery = ditto store collection('cars') find("name == 'toyota'") observelocal((cars) => { numberofcars = cars length }) function additem () { ditto store collection('cars') upsert({ "name" 'toyota', "state" 'for sale' }) } log out let loggedin = false if (auth0 isauthenticated()) { loggedin = true }if (loggedin) { // render the logout button \<button onclick={onlogoutclick}>logout\</button> } else { \<button onclick={login}>login\</button> } and then we can write the logout function and attach it to the button we also recommend calling ditto auth logout with a callback function that evicts any data from the local database function onlogoutclick() { ditto auth logout(() => { ditto store collection('cars') findall() evict() }) await auth0 logout({ returnto window\ location origin }) } to make this usable for real world applications, you can retrieve the user's profile details such as email, username, and full name see the official auth0 documentation for your platform to add that functionality to your application yay! you now have a fully functioning onlinewithauthentication app build and run it on a device for a full application example, see the https //github com/getditto/examples permission server the authentication webhook needs to handle an http post request each client that will need to authenticate will send a payload to this webhook the following section requires that you have knowledge of writing server side http endpoints and responding with a json payload code samples of server side code are written with a nodejs / express syntax you can use any language or framework on the server side incoming post body when your client device wants to authenticate using your webhook, your server will receive an http post with a json payload that looks like { "appid" "your app id here", // the appid "provider" "my auth", // this is the "name" of the "authentication webhook" "token" "eyjhbgcioiji " // this is what each device will send to authenticate } your can introspect these values by parsing out the request body let express = require('express') let cors = require('cors') let body = require('body parser') let app = express() app use(cors()) app use(body json()) let app = express() app post('/', (req, res) => { const appid = req body appid const provider = req body provider const token = req body token })let express = require('express') let cors = require('cors') let body = require('body parser') let app = express() app use(cors()) app use(body json()) let app = express() app post('/', (req, res) => { const appid = req body appid const provider = req body provider const token = req body token }) generally, you will want to check the token for some sort of validity let's assume you have some sort of library or logic to parse and validate the token is for a specific user you can also use the clientinfo key in your json response to pass information back to client app post('/', async (req, res) => { const token = req body token; try { // the token that your server receives from ditto is always a string let parsedtoken = json parse(token) let payload = getdittopermissions(parsedtoken) res json(payload) } catch (err) { res json({ "authenticate" err, "clientinfo" err message }) } })app post('/', async (req, res) => { const token = req body token; try { // the token that your server receives from ditto is always a string let parsedtoken = json parse(token) let payload = getdittopermissions(parsedtoken) res json(payload) } catch (err) { res json({ "authenticate" err, "clientinfo" err message }) } }) as a simple example, let's grant full read & write permissions to all collections and all documents app post('/', async (req, res) => { const token = req body token; try { let payload = { "authenticate" true, "expirationseconds" 28800, "userid" "123abc", "permissions" { "read" { "everything" true, "queriesbycollection" {} }, "write" { "everything" true, "queriesbycollection" {} } } } res json(payload) } catch (err) { res json({ "authenticate" err, "clientinfo" err message }) } })app post('/', async (req, res) => { const token = req body token; try { let payload = { "authenticate" true, "expirationseconds" 28800, "userid" "123abc", "permissions" { "read" { "everything" true, "queriesbycollection" {} }, "write" { "everything" true, "queriesbycollection" {} } } } res json(payload) } catch (err) { res json({ "authenticate" err, "clientinfo" err message }) } }) for more information on how to design your app's permissions, see access control permissions deploy your server now, the portal will attempt to reach this server that means you must deploy it somewhere that this http request is accessible for testing, you can use a quick deploy service such as glitch please be sure that this endpoint is not behind a firewall or vpn if you cannot get around this requirement mailto\ support\@ditto live