> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ditto.live/llms.txt
> Use this file to discover all available pages before exploring further.

# Online 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 with authentication mode to initialize Ditto and start prototyping.

<Callout icon="circle-info" color="#3B82F6" iconType="regular">
  This content is for SDK V4. For the latest version, see the [V5 documentation](/sdk/latest).
</Callout>

Learn more about Ditto's authentication methods in [Authentication and Authorization](/key-concepts/authentication-and-authorization).

## API

To see fully working examples, see the [code samples on GitHub](https://github.com/getditto/examples-permission "code samples on GitHub").

### Creating Your Client

To start with online authentication you need to use the **onlineWithAuthentication** identity type when creating your Ditto client. This identity type requires you to provide a two callbacks to handle authentication requests from Ditto:

* **authenticationRequired**: Called when Ditto initially needs to authenticate
* **authenticationExpiringSoon**: Called when Ditto needs to refresh the authentication token

<Warning>
  If you do not implement **authenticationExpiringSoon**, sync will stop when the token expires.
</Warning>

Each of these callbacks provides an **authenticator** object which can be used to call **login** with a token, your webhook name and a callback to handle the result of the login attempt. The callback will receive two values:

* **clientInfo**: A JSON object provided by your webhook.
* **error**: If the login attempt failed, this will contain an error object with details about the failure. If the login was successful, this will be null.

<Note>
  The method to access the values passed to the callback differs between languages. See the code examples below for details.
</Note>

<CodeGroup>
  ```swift Swift theme={null}
  class AuthDelegate: DittoAuthenticationDelegate {
      func authenticationRequired(authenticator: DittoAuthenticator) {
          // Called when Ditto initially needs to authenticate
          authenticator.login(token: accessToken, provider: "my-auth-webhook") { clientInfo, err in
              // If err is nil, login was successful
              print("Login request completed.")
          }
      }

      func authenticationExpiringSoon(
          authenticator: DittoAuthenticator,
          secondsRemaining: Int64
      ) {
          // Called when Ditto needs to refresh the authentication token
          authenticator.login(token: accessToken, provider: "my-auth-webhook") { clientInfo, err in
              // If err is nil, login was successful
              print("Login request completed.")
          }
      }
  }

  let identity = DittoIdentity.onlineWithAuthentication(
      appID: "REPLACE_ME_WITH_YOUR_APP_ID",
      enableDittoCloudSync: false, // This is required to be set to false to use the correct URLs
      customAuthURL: URL(string: "REPLACE_ME_WITH_YOUR_AUTH_URL"),
      authenticationDelegate: AuthDelegate()
  )

  // Set the Ditto Websocket URL
  ditto.updateTransportConfig { transportConfig in
      transportConfig.connect.webSocketURLs.insert("wss://REPLACE_ME_WITH_YOUR_WEBSOCKET_URL")
  } 

  // Disable DQL strict mode so that collection definitions are not required in DQL queries
  try await ditto.store.execute("ALTER SYSTEM SET DQL_STRICT_MODE = false")

  // disable sync with v3 peers, required for DQL
  do {
      try ditto.disableSyncWithV3()
  } catch let error {
      print(
          "DittoManger - ERROR: disableSyncWithV3() failed with error \"\(error)\""
      )
  }

  let ditto = Ditto(identity: identity)
  try! ditto.startSync()
  ```

  ```kotlin Kotlin theme={null}
  class AuthCallback: DittoAuthenticationCallback {
      override fun authenticationRequired(authenticator: DittoAuthenticator) {
          // Called when Ditto initially needs to authenticate
          authenticator.login(accessToken, "my-auth-webhook") { clientInfo, error ->
              // If error is null, login was successful
              println("Login request completed.")
          }
      }

      override fun authenticationExpiringSoon(
          authenticator: DittoAuthenticator,
          secondsRemaining: Long
      ) {
          // Called when Ditto needs to refresh the authentication token
          authenticator.login(accessToken, "my-auth-webhook") { clientInfo, error ->
              // If error is null, login was successful
              println("Login request completed.")
          }
      }
  }

  val androidDependencies = DefaultAndroidDittoDependencies(context)
  val identity = DittoIdentity.OnlineWithAuthentication(
      dependencies = androidDependencies,
      appId = "REPLACE_ME_WITH_YOUR_APP_ID",
      customAuthUrl = "REPLACE_ME_WITH_YOUR_AUTH_URL",
      enableDittoCloudSync = false, // This is required to be set to false to use the correct URLs
      callback = AuthCallback()
  )

  val ditto = Ditto(androidDependencies, identity)

  ditto?.updateTransportConfig { config ->
      config.connect.websocketUrls.add("REPLACE_ME_WITH_YOUR_WEBSOCKET_URL")
  }

  // Disable DQL strict mode so that collection definitions are not required in DQL queries
  ditto.store.execute("ALTER SYSTEM SET DQL_STRICT_MODE = false")

  // disable sync with v3 peers, required for DQL
  ditto.disableSyncWithV3()

  ditto.startSync()
  ```

  ```js JS theme={null}
  const authHandler = {
      authenticationRequired: async function (authenticator: Authenticator) {
      // Called when Ditto initially needs to authenticate
          authenticator.login('accessToken', 'my-auth-webhook').then(({ clientInfo, error }) => {
              // If error is null, login was successful
              console.log('Login request completed.');
          });
      },
      authenticationExpiringSoon: async function (
        authenticator: Authenticator,
        secondsRemaining: number,
      ) {
          // Called when Ditto needs to refresh the authentication token
          authenticator.login('accessToken', 'my-auth-webhook').then(({ clientInfo, error }) => {
              // If error is null, login was successful
              console.log('Login request completed.');
          });
      },
  };

  const identity = {
      type: 'onlineWithAuthentication',
      appID: "REPLACE_ME_WITH_YOUR_APP_ID",
      customAuthURL: "REPLACE_ME_WITH_YOUR_AUTH_URL",
      enableDittoCloudSync: false, // This is required to be set to false to use the correct URLs
      authHandler
  }

  ditto = new Ditto(identity)

  ditto.updateTransportConfig((config) => {
    config.connect.websocketURLs.push(
      'wss://REPLACE_ME_WITH_YOUR_WEBSOCKET_URL'
    );
  });

  // Disable DQL strict mode so that collection definitions are not required in DQL queries
  await ditto.store.execute("ALTER SYSTEM SET DQL_STRICT_MODE = false");

  // disable sync with v3 peers, required for DQL
  await ditto.disableSyncWithV3();

  ditto.sync.start() // Use ditto.startSync() in 4.11 and earlier
  ```

  ```typescript TS theme={null}
  import { Ditto, Authenticator, AuthenticationHandler, TransportConfig, Identity, LoginResult } from '@dittolive/ditto'

  const authHandler: AuthenticationHandler = {
    authenticationRequired: async (authenticator: Authenticator): Promise<void> => {
      // Called when Ditto initially needs to authenticate
      const result: LoginResult = await authenticator.login(accessToken, "my-auth-webhook")
      // If result.error is null, login was successful
      console.log("Login request completed. Error?", result.error)
    },
    authenticationExpiringSoon: (authenticator: Authenticator, secondsRemaining: number): void => {
      // Called when Ditto needs to refresh the authentication token
      authenticator.login(accessToken, "my-auth-webhook").then((result: LoginResult) => {
        // If result.error is null, login was successful
        console.log("Login request completed. Error?", result.error)
      })
    }
  }

  const identity: Identity = {
    type: 'onlineWithAuthentication',
    appID: "REPLACE_ME_WITH_YOUR_APP_ID",
    customAuthURL: "REPLACE_ME_WITH_YOUR_AUTH_URL",
    enableDittoCloudSync: false, // This is required to be set to false to use the correct URLs
    authHandler
  }

  const ditto = new Ditto(identity)
  const config: TransportConfig = new TransportConfig()
  config.connect.websocketURLs.push('wss://REPLACE_ME_WITH_YOUR_WEBSOCKET_URL')

  ditto.setTransportConfig(config)
  ditto.startSync()
  ```

  ```java Java theme={null}
  public class AuthCallback implements DittoAuthenticationCallback {
      @Override
      public void onAuthenticationRequired(DittoAuthenticator authenticator){
          // Called when Ditto initially needs to authenticate.
          authenticator.login(accessToken, "my-auth-webhook", (clientInfo, error) -> {
              // If error is null, login was successful.
              System.out.println("Login request completed.");
          });
      }

      @Override
      public void onAuthenticationExpiringSoon(
              DittoAuthenticator authenticator,
              long secondsRemaining
      ) {
          // Called when Ditto needs to refresh the authentication token
          authenticator.login(accessToken, "my-auth-webhook", (clientInfo, error) -> {
              // If error is null, login was successful.
              System.out.println("Login request completed.");
          });
      }
  }

  DittoIdentity identity = new DittoIdentity.OnlineWithAuthentication(
      "REPLACE_ME_WITH_YOUR_APP_ID",
      new AuthCallback(),
      false, // This is required to be set to false to use the correct URLs
      "REPLACE_ME_WITH_YOUR_AUTH_URL"
  );

  // Create a directory
  File dittoDir = new File("path/to/ditto/dir");

  DittoConfig dittoConfig = new DittoConfig.Builder(dittoDir)
                  .identity(identity)
                  .build();

  Ditto ditto = new Ditto(dittoConfig);

  ditto.updateTransportConfig(transportConfig -> {
      transportConfig.connect(connect -> {
          // Set the Ditto Websocket URL
          connect.websocketUrls().add("wss://REPLACE_ME_WITH_YOUR_WEBSOCKET_URL");
      });
  });

  // Disable DQL strict mode so that collection definitions are not required in DQL queries
  ditto.getStore().execute("ALTER SYSTEM SET DQL_STRICT_MODE = false");


  ditto.startSync();
  ```

  ```csharp C# theme={null}
  class AuthDelegate : IDittoAuthenticationDelegate
  {
      public async void AuthenticationRequired(DittoAuthenticator authenticator)
      {
          // Called when Ditto needs to refresh the authentication token
          try
          {
              // On success returns client info
              // On failure throws DittoAuthenticationException
              string clientInfo = await authenticator.LoginAsync(accessToken, "my-auth-webhook");
          }
          catch (DittoAuthenticationException ex)
          {
              // clientInfo is in ex.ClientInfoJson
              Console.WriteLine($"Authentication failed: {ex.Message}");
          }
      }

      public async void AuthenticationExpiringSoon(DittoAuthenticator authenticator, long secondsRemaining)
      {
          // Called when Ditto initially needs to authenticate
          try
          {
              // On success returns client info
              // On failure throws DittoAuthenticationException
              string clientInfo = await authenticator.LoginAsync(accessToken, "my-auth-webhook");
          }
          catch (DittoAuthenticationException ex)
          {
              // clientInfo is in ex.ClientInfoJson
              Console.WriteLine($"Authentication failed: {ex.Message}");
          }
      }
  }

  var identity = DittoIdentity.OnlineWithAuthentication(
      appId: "REPLACE_ME_WITH_YOUR_APP_ID",
      authDelegate: new AuthDelegate(),
      enableDittoCloudSync: false, // This is required to be set to false to use the correct URLs
      customAuthURL: "REPLACE_ME_WITH_YOUR_AUTH_URL"
  );

  var ditto = new Ditto(identity);

  ditto.UpdateTransportConfig(config =>
  {
      config.EnableAllPeerToPeer();
      // Set the Ditto Websocket URL
      config.Connect.WebSocketUrls.Add("wss://REPLACE_ME_WITH_YOUR_WEBSOCKET_URL");
  });

  // Disable DQL strict mode so that collection definitions are not required in DQL queries
  await ditto.Store.ExecuteAsync("ALTER SYSTEM SET DQL_STRICT_MODE = false");

  // disable sync with v3 peers, required for DQL
  ditto.DisableSyncWithV3();

  ditto.Sync.Start(); // Use ditto.StartSync() in 4.11 and earlier
  ```

  ```c++ C++ theme={null}
  class AuthCallback : public ditto::AuthenticationCallback {
      void authentication_required(std::shared_ptr<ditto::Authenticator> authenticator) override {
          // Called when Ditto initially needs to authenticate
          authenticator->login(accessToken, "my-auth-webhook", [](std::unique_ptr<std::string> client_info, std::unique_ptr<ditto::DittoError> error) {
              // If error is null, login was successful
              std::cout << "Login request completed\n";
          });
      }

      void authentication_expiring_soon(std::shared_ptr<ditto::Authenticator> authenticator,
                                       std::int64_t seconds_remaining) override {
          // Called when Ditto needs to refresh the authentication token
          authenticator->login(accessToken, "my-auth-webhook", [](std::unique_ptr<std::string> client_info, std::unique_ptr<ditto::DittoError> error) {
              // If error is null, login was successful
              std::cout << "Login request completed\n";
          });
      }

      void authentication_status_did_change(std::shared_ptr<ditto::Authenticator> authenticator) override {
          // Called when the authentication status changes
          std::cout << "Authentication status changed\n";
      }
  };

  const auto identity = ditto::Identity::OnlineWithAuthentication(
      "REPLACE_ME_WITH_YOUR_APP_ID",
      std::make_shared<AuthCallback>(),
      false,  // This is required to be set to false to use the correct URLs
      "REPLACE_ME_WITH_YOUR_AUTH_URL"
  );

  ditto->update_transport_config([](ditto::TransportConfig &config) {
      config.enable_all_peer_to_peer();
      // Set the Ditto Websocket URL
      config.connect.websocket_urls.insert("REPLACE_ME_WITH_YOUR_WEBSOCKET_URL");
  });

  // Disable DQL strict mode so that collection definitions are not required in DQL queries
  ditto->get_store().execute("ALTER SYSTEM SET DQL_STRICT_MODE = false");

  // Required for compatibility with DQL.
  ditto->disable_sync_with_v3();

  ditto->start_sync();
  ```

  ```rust Rust theme={null}
  pub struct AuthHandler;

  impl DittoAuthenticationEventHandler for AuthHandler {
      fn authentication_required(&self, authenticator: DittoAuthenticator) {
          // Called when Ditto initially needs to authenticate.
          match authenticator.login("accessToken", "test-webhook") {
              Ok(result) => {
                  // clientInfo is in result.feedback
                  println!("Authentication succeeded");
              }
              Err(e) => {
                  // Can get clientInfo by calling e.get_authentication_client_feedback()
                  println!("Authentication failed: {}", e);
              }
          }
      }

      fn authentication_expiring_soon(
          &self,
          authenticator: DittoAuthenticator,
          seconds_remaining: Duration,
      ) {
          // Called when Ditto needs to refresh the authentication token
          match authenticator.login("accessToken", "test-webhook") {
              Ok(result) => {
                  // clientInfo is in result.feedback
                  println!("Authentication succeeded");
              }
              Err(e) => {
                  // Can get clientInfo by calling e.get_authentication_client_feedback()
                  println!("Authentication failed: {}", e);
              }
          }
      }
  }

  let ditto = Ditto::builder()
      .with_root(Arc::new(PersistentRoot::from_current_exe()?)) // Persistent storage location
      .with_identity(|ditto_root| OnlineWithAuthentication::new(
          ditto_root,
          AppId::from_str("REPLACE_ME_WITH_YOUR_APP_ID")?,
          AuthHandler,
          false, // This is required to be set to false to use the correct URLs
          Some("REPLACE_ME_WITH_YOUR_AUTH_URL")
      ))?
      .build()?;

  ditto.update_transport_config(|config| {
      config.enable_all_peer_to_peer();
      // Set the Ditto Websocket URL
      config.connect.websocket_urls.insert("REPLACE_ME_WITH_YOUR_WEBSOCKET_URL");
  });

  _ = ditto
      .store()
      .execute_v2("ALTER SYSTEM SET DQL_STRICT_MODE = false")
      .await?;

  _ = ditto.disable_sync_with_v3();
  _ = ditto.start_sync();
  ```

  ```dart Dart theme={null}
  final authHandler = AuthenticationHandler(
    authenticationRequired: (authenticator) {
      // Called when Ditto initially needs to authenticate
      authenticator.login(token: "accessToken", provider: "my-auth-webhook").then((result) {
        // clientInfo is in result.clientInfo
        // If result.error is null, login was successful
        print("Login request complete");
      });
    },
    authenticationExpiringSoon: (authenticator, secondsRemaining) {
      // Called when Ditto needs to refresh the authentication token
      authenticator.login(token: "accessToken", provider: "my-auth-webhook").then((result) {
        // clientInfo is in result.clientInfo
        // If result.error is null, login was successful
        print("Login request complete");
      });
    },
  );

  final identity = OnlineWithAuthenticationIdentity(
      appID: appID,
      authenticationHandler: authHandler,
      enableDittoCloudSync: false, // This is required to be set to false to use the correct URLs
      customAuthUrl: authUrl
  );

  final ditto = await Ditto.open(identity);

  ditto.updateTransportConfig((config) {
      // Note: this will not enable peer-to-peer sync on the web platform
      config.setAllPeerToPeerEnabled(true);
      // Set the Ditto Websocket URL
      config.connect.webSocketUrls.add('REPLACE_ME_WITH_YOUR_WEBSOCKET_URL');
  });

  // Disable DQL strict mode so that collection definitions are not required in DQL queries
  await ditto.store.execute("ALTER SYSTEM SET DQL_STRICT_MODE = false");

  ditto.startSync();
  ```
</CodeGroup>

<Note>
  In the examples above `accessToken` is a placeholder for the actual token sent to your webhook.
  Depending on your webhook implementation you may need to refresh this token periodically.
</Note>

## Login

Login takes two parameters:

* **Token**: This can be any string value. Most auth services use a [JWT (JSON Web Token)](https://jwt.io/ "JWT (JSON Web Token)"), 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.
* **Provider**: This 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.

<Frame>
  <img src="https://mintcdn.com/ditto-248bc0d1/_UNdP98-Q-K7lTyJ/images/v4.9/authentication-1.webp?fit=max&auto=format&n=_UNdP98-Q-K7lTyJ&q=85&s=afe53410b936f8956656eaa213f9b9d0" width="600" height="116" data-path="images/v4.9/authentication-1.webp" />
</Frame>

<br />

### When should you call **ditto.auth.login**?

Along with the callbacks above, you can also call **ditto.auth.login** manually. This is useful if you know specific times when your app will be online.

For an example implementation of authentication for Android, see the Ditto open-source demo chat app's "[authentication](https://github.com/getditto/demoapp-chat/tree/authentication "authentication")" branch in the getditto GitHub repository.

<CodeGroup>
  ```swift Swift theme={null}
  ditto.auth.login(token: accessToken, provider: "my-auth-webhook") { clientInfo, err in
      // If err is nil, login was successful
      print("Login request completed.")
  }
  ```

  ```kotlin Kotlin theme={null}
  ditto.auth.login(accessToken, "my-auth-webhook") { clientInfo, error ->
      // If error is null, login was successful
      println("Login request completed.")
  }
  ```

  ```js JS theme={null}
  ditto.auth.login(accessToken, "my-auth-webhook").then(({clientInfo, error}) => {
      // If error is null, login was successful
      console.log("Login request completed.);
  });
  ```

  ```typescript TS theme={null}
  import { Ditto, LoginResult } from '@dittolive/ditto'

  ditto.auth.login(accessToken, "my-auth-webhook").then((result: LoginResult) => {
    // If result.error is null, login was successful
    console.log("Login request completed. Error?", result.error)
  })
  ```

  ```java Java theme={null}
  ditto.getAuth().login(accessToken, "my-auth-webhook", (success, error) -> {
      // If error is null, login was successful.
      System.out.println("Login request completed.);
  });
  ```

  ```csharp C# theme={null}
  try
  {
      // On success returns client info
      // On failure throws DittoAuthenticationException
      string clientInfo = await authenticator.LoginAsync(accessToken, "my-auth-webhook");
  }
  catch (DittoAuthenticationException ex)
  {
      // clientInfo is in ex.ClientInfoJson
      Console.WriteLine($"Authentication failed: {ex.Message}");
  }
  ```

  ```c++ C++ theme={null}
  ditto->get_auth()->login(accessToken, "my-auth-webhook", [](std::unique_ptr<std::string> client_info, std::unique_ptr<ditto::DittoError> error) {
      // If error is null, login was successful
      std::cout << "Login request completed\n";
  });
  ```

  ```rust Rust theme={null}
  match ditto.auth() {
      // Called when Ditto needs to refresh the authentication token
      Ok(result) => {
          // clientInfo is in result.feedback
          println!("Authentication succeeded");
      }
      Err(e) => {
          // Can get clientInfo by calling e.get_authentication_client_feedback()
          println!("Authentication failed: {}", e);
      }
  }
  ```

  ```dart Dart theme={null}
  ditto.auth.login(token: accessToken, provider: "my-auth-webhook").then((result) {
      // If result.error is null, login was successful
      print("Login request complete");
  });
  ```

  ```go Go theme={null}
  clientInfoJSON, err := dit.Auth().Login(accessToken, ditto.AuthenticationProvider("my-auth-webhook"))
  if err != nil {
      log.Errorf("Authentication failed: %v", err)
  } else {
      log.Printf("Authentication successful; client info: %v", clientInfoJSON)
  }
  ```
</CodeGroup>

## 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.

<CodeGroup>
  ```swift Swift theme={null}
  ditto.auth.logout(cleanup: { ditto in
      ditto.store.execute("EVICT FROM tasks");
  })
  ```

  ```kotlin Kotlin theme={null}
  ditto.auth.logout { ditto ->
      ditto.store.execute("EVICT FROM tasks");
  }
  ```

  ```js JS theme={null}
  ditto.auth.logout((ditto) => {
      ditto.store.execute('EVICT FROM cars');
  });
  ```

  ```typescript TS theme={null}
  import { Ditto } from '@dittolive/ditto'

  ditto.auth.logout((ditto: Ditto): void => {
    ditto.store.execute('EVICT FROM cars')
  })
  ```

  ```java Java theme={null}
  ditto.getAuth().logout((ditto) -> {
      ditto.getStore().execute("EVICT FROM cars");
  });
  ```

  ```csharp C# theme={null}
  ditto.Auth.Logout((ditto) =>
  {
      ditto.Store.ExecuteAsync("EVICT FROM cars");
  });
  ```

  ```c++ C++ theme={null}
  ditto->get_auth()->logout([](ditto::Ditto ditto) {
      ditto.get_store().execute("EVICT FROM cars");
  });
  ```

  ```rust Rust theme={null}
  match ditto.auth() {
      Some(auth) => {
          auth.logout(|ditto| {
              ditto.store().execute_v2("EVICT FROM cars");
          });
      }
      None => return Err(anyhow!("Ditto authentication not configured")),
  }
  ```

  ```dart Dart theme={null}
  await ditto.auth.logout();
  await ditto.store.execute("EVICT FROM cars");
  ```

  ```go Go theme={null}
  dit.Auth().Logout(func (dit *ditto.Ditto) {
      result, err := dit.Store().Execute("EVICT FROM cars", nil)
      if err != nil {
          log.Errorf("EVICT failed: %v", err)
      } else {
          result.Close()  // cleanup
      }
  })
  ```
</CodeGroup>

## 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 [Express](https://expressjs.com/ "Express")-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 third-party provider using [Auth0](https://auth0.com/ "Auth0"). 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 [code samples on GitHub](https://github.com/getditto/examples-permission "code samples on GitHub").

### Configure Ditto

​To use an "Online With Authentication" identity, go to your app in the [portal](https://portal.ditto.live/ "portal") and find the **Authentication Mode & Webhook Settings** section. Ensure that **"With Authentication"** is selected like so:

<Frame>
  <img src="https://mintcdn.com/ditto-248bc0d1/_UNdP98-Q-K7lTyJ/images/v4.9/authentication-2.webp?fit=max&auto=format&n=_UNdP98-Q-K7lTyJ&q=85&s=4347e6efcd05ed21b663addaa54871f8" width="600" height="204" data-path="images/v4.9/authentication-2.webp" />
</Frame>

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 name.
* 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 [this example](https://github.com/getditto/sample-authentication-permissions/tree/main/server/simple) webhook to test your application. However, you should use your own webhook in production. The example simply authenticates all requests for 7 days of offline usage.

Once configured, you should see a webhook that looks like this in your portal app settings:

<Frame>
  <img src="https://mintcdn.com/ditto-248bc0d1/_UNdP98-Q-K7lTyJ/images/v4.9/authentication-3.webp?fit=max&auto=format&n=_UNdP98-Q-K7lTyJ&q=85&s=14b788bc941eacc52164b406e0b20ecc" width="600" height="64" data-path="images/v4.9/authentication-3.webp" />
</Frame>

### Configure Auth0

The second step is to configure Auth0. Follow these steps:

1. Create a new Auth0 application.
2. Configure the allowed callbacks and origins for your application. Make sure to add the callback URL for your application.
3. Configure the allowed grant types for your application. For this tutorial, we will use the "Authorization Code" grant type.
4. 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...**

...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...**

...you can [sign up for one here](https://auth0.com/signup "sign up for one here") - it's free.

**Register your app with Auth0**

1. 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.
2. Create a new registration for your app. Do this by clicking the Create application button near the top right of the page.

<Frame>
  <img src="https://mintcdn.com/ditto-248bc0d1/_UNdP98-Q-K7lTyJ/images/v4.9/authentication-4.webp?fit=max&auto=format&n=_UNdP98-Q-K7lTyJ&q=85&s=d930d2e41a094722f03486283f5d6014" width="600" height="230" data-path="images/v4.9/authentication-4.webp" />
</Frame>

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

[Auth0 Tutorial](https://auth0.com/blog/get-started-with-android-authentication-using-kotlin-part-1/ "Auth0 Tutorial")

[Auth0 Website - Android Getting Started / Onboarding](https://manage.auth0.com/dashboard/us/dev-voiik8orqv6m487g/onboarding "Auth0 Website - Android Getting Started / Onboarding")

### Integrating Auth0 with Ditto

Assuming you have a login button in the HTML:

```JS JS theme={null}
<button onClick={login}>Login</button>
```

We attach a **login** function to the button.

```js JS theme={null}
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.

<Info>
  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.
</Info>

```js JS theme={null}
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",
      customAuthURL: "REPLACE_ME_WITH_YOUR_AUTH_URL",
      enableDittoCloudSync: false, // This is required to be set to false to use the correct URLs
      authHandler
    }

    ditto = new Ditto(identity, '/persistence/file/path')
    const config = new TransportConfig()
    config.connect.websocketURLs.push('wss://REPLACE_ME_WITH_YOUR_WEBSOCKET_URL')

    ditto.setTransportConfig(config)
    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 JSX theme={null}
<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:

```JS JS theme={null}
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**

```JS JS theme={null}
let loggedIn = false
if (auth0.isAuthenticated()) {
  loggedIn = true
}
```

```JS JS theme={null}
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.

```JS JS theme={null}
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 [example application on GitHub](https://github.com/getditto/examples-permission "example application on GitHub")

## 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:

```json JSON theme={null}
{
  "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:

```js JS theme={null}
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.

```js JS theme={null}
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.

```js JS theme={null}
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 [Data Authorization](./data-authorization).

### 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.

<Info>
  Please be sure that this endpoint is not behind a firewall or VPN. If you cannot get around this requirement [contact us](https://support.ditto.live/hc/en-us).
</Info>
