Dynamic is a powerful web3 authentication platform that provides simple onboarding flows for all users, coupled with advanced login experiences for crypto-native individuals. Beyond mere authentication, Dynamic offers developer tools that enhance user interactions, ensuring seamless integration across various web applications.

Co:Create pairs well will Dynamic. Co:Create handles web3 capabilities related to seamlessly generating user wallets and managing token operations, while Dynamic handles the user authentication process for your application to enable you to provide personalized experiences to your users.

This tutorial guides you through integrating Dynamic for user authentication and Co:Create APIs for wallet generation.

Overview

While Co:Create offers powerful web3 capabilities like user wallets, token controls, and token distribution, the platform leaves user authentication to be handled externally for two primary reasons:

  • Flexibility: With Co:Create, you can choose your preferred authentication method, tailoring it to your project’s unique needs.
  • Security: Authentication is intricate and demands specialized expertise. Co:Create chooses to focus on its web3 competencies, delegating authentication to be handled externally enables more secure authentication for your application.

Therefore, it’s on you as the application developer to deliver your preferred authentication implementation.

📘 API Key ≠ User Authentication

While Co:Create uses API keys for requests, it’s crucial to understand that API keys are for identifying and authorizing applications, not individual users.

An API key ensures that the application has permission to access the platform but doesn’t distinguish between individual users of that application.

For user-specific functionalities and personalized experiences, user-level authentication is essential.

Tutorial

This tutorial uses the following stack: Next.js, React, Chakra and Typescript.

👍 Explore the Tutorial Application

To help you grasp the integration process, we’ve provided a hands-on tutorial application.

Reviewing this application will give you a practical understanding of how Dynamic and Co:Create can be seamlessly integrated.

Access the complete code for this tutorial on GitHub.

View a live demo here.

Key Implementation Steps:

These are steps we’ll be walking through:

  1. Dynamic Setup: Register on Dynamic’s website, configure email sign-in in the dashboard, and retrieve your Dynamic environment ID and public key for later use.
  2. Co:Create Setup: Sign up at Co:Create’s sandbox and generate an API key.
  3. SDK Initialization: Incorporate Dynamic’s SDK into your app using DynamicContextProvider.
  4. Authentication: Use Dynamic’s useDynamicContext() hook to access authentication methods and states.
  5. UI Integration: Add a button for initiating the Dynamic authentication flow.
  6. Co:Create Backend: Authenticate user JWTs and use Co:Create’s API to generate web3 wallets for authenticated users.

1. Dynamic setup

Get started by Signing up for Dynamic and creating your account. Once your account is created, you should be in the dynamic sandbox environment by default.

For this tutorial, we will be using the Email sign-in feature from Dynamic.

  1. Navigate to Configurations
  2. Click on “Sign in with Email or Social”.
  3. Since Co:Create will be providing the user wallet, enable the “Email Sign up (No wallet)” option. Disable the other options as illustrated below.
  1. Next, copy your environmentID and publicKey from the developer page and store them somewhere accessible. These will be used later on in the app.
  1. Create a Dynamic API Token from the developer page, copy it and save it securely.

2. Co:Create Setup

  1. Head to the Co:Create Sandbox.
  2. Sign up either with your email address or Google Sign-in.
  3. Click the profile icon in the top right and navigate to your Org settings.
  4. Click on the “API Keys” tab and create an API Key.

3. Initialize the Dynamic SDK

We are going to use the DynamicContextProvider component, which will initialize the SDK and make it available to the rest of the application. It’s worth placing this as high up the component tree as possible to make sure Dynamic is reachable from wherever you need it. In our demo app, it is initialized in _app.tsx.

Make sure to replace environmentID with the Dynamic environmentId obtained previously.

You can read more about DynamicContextProvider here.

// src/pages/_app.tsx
import { DynamicContextProvider, DynamicWidget } from "@dynamic-labs/sdk-react-core";

const App = () => (
  <DynamicContextProvider
    settings={{
      environmentId: <environmentID>,
    }}
  >
    <DynamicWidget />
  </DynamicContextProvider>
);

export default App;

4. Authentication - Setup Dynamic context in your app

We are going to use the useDynamicContext() hook to get all the context that our app needs from Dynamic. A comprehensive set of methods and variables available from useDynamicContext() can be found here.

// src/pages/index.tsx
import { useDynamicContext, useIsLoggedIn } from "@dynamic-labs/sdk-react-core";

export default function Home() {
...
const { authToken, handleLogOut, user, setShowAuthFlow } =
    useDynamicContext();

const isLoggedIn = useIsLoggedIn();
...
}

5. UI Implementation - Create a Sign Up / Sign In button

Here is a snippet for a simple button that only shows when the user is not authenticated. The onClick for this button calls the setShowAuthFlow function that was returned from useDynamicContext. Using a couple of lines, we have successfully added a straightforward authentication mechanism to our app!

{
  !isLoggedIn && (
    <Button colorScheme="purple" onClick={() => setShowAuthFlow(true)}>
      Sign Up or Sign In
    </Button>
  );
}

Sign Up / Sign in Flow

Here is how the Sign Up / Sign in Flow looks in the app:

6. Co:Create Wallet Creation

Now that you have successfully authenticated the user with Dynamic, we can move on to the Co:Create Wallet Creation. After the user authenticates with Dynamic, we get a JWT back that can be passed to our backend for authentication.

Co:Create APIs are only intended to be used in the backend. The Co:Create API key should never be exposed in the frontend.

We will first create a simple jwt authentication function that validates the JWT that we get from Dynamic. Make sure to replace PUBLIC_KEY with the public key obtained from Dynamic’s dashboard above. The jsonwebtoken package takes in the public key and jwt, validates that the jwt is valid and returns the decoded claims.

// src/auth.ts
import jwt from "jsonwebtoken";

const PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr3+h1sv4HP5YGxTs+JM6
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
sG1siVpS7+H2EXGxwn5AS58CAwEAAQ==
-----END PUBLIC KEY-----`;

export const authenticateJWT = (token: string) => {
  try {
    const decoded = jwt.verify(token, PUBLIC_KEY);

    if (!(typeof decoded === "object" && "email" in decoded)) {
      throw new Error("Invalid token");
    }

    return decoded;
  } catch (error) {
    console.error(error);
    throw error;
  }
};

This authenticateJWT function will be used in our backend to create the user and wallet using Co:Create APIs. Here we verify that the token is valid and extract the email address.

We use the email address to send a POST request to the Co:Create user creation API endpoint. Docs on the Co:Create User APIs can be found here. We will return the response from the creation API endpoint back to the client. This response should contain the Co:Create wallet address for the user.

// src/api/create_user.ts

const decoded = authenticateJWT(token);

// Check if decoded is an object and has an email property
if (!(typeof decoded === "object" && "email" in decoded)) {
  return res.status(400).json({ error: "Invalid token" });
}
// Make a request to the CoCreate API to create a user
const options = {
  method: "POST",
  url: "https://api.testcocrt.xyz/alpha/user",
  headers: {
    accept: "application/json",
    "content-type": "application/json",
    Authorization: `Bearer ${process.env.COCREATE_API_KEY}`,
  },
  data: {
    email: decoded.email,
  },
};
// Rest of your code...
const response = await axios.request(options);

return res.status(200).json(response.data);

7. Dynamic Wallet Linking

Once you successfully create a wallet with Co:Create, you can use Dynamic’s Wallet API to associate the Co:Create Wallet Address to the dynamic user profile. This step requires the Dynamic API Token that was created in Step 1. Add the dynamic API Token to your environment file using DYNAMIC_API_KEY=dyn_XXXXXXXXXXXXXXXXXXXXXXXXXX.

You can then use axios to send a POST request to Dynamic to associate the Co:Create Wallet Address with Dynamic’s user profile.

const dynamicOptions = {
  method: "POST",
  url: `https://app.dynamicauth.com/api/v0/users/${dynamic_user_id}/wallets`,
  headers: {
    accept: "application/json",
    "content-type": "application/json",
    Authorization: `Bearer ${process.env.DYNAMIC_API_KEY}`,
  },
  data: {
    chain: "EVM",
    publicWalletAddress: cocreate_wallet_address,
    walletName: "Co:Create Wallet",
    walletProvider: "custodialService",
  },
};

await axios.request(dynamicOptions);

After completing this step, you should start seeing users and their wallet addresses in the Dynamic User Dashboard.

Summary

This tutorial guides you through integrating Dynamic and Co:Create into a web application. Dynamic handles user authentication with email sign-in, while Co:Create facilitates web3 wallet generation.

You can find more technical details about Dynamic and Co:Create in their respective docs:

The tutorial’s full code can be accessed in Co:Create’s GitHub repo.

To view the experience yourself, navigate to the live demo.