Skip to content

Authentication

Learn how to authenticate users and manage access tokens in your De. application.

Authentication Overview

De. provides multiple authentication methods to suit different use cases:

  • Phone number authentication with SMS verification
  • Email/password authentication
  • OAuth integration (Google, Apple, etc.)
  • API key authentication for server-to-server

Authentication Flow

Authentication Flow
User enters credentials de.auth validates Access token issued User authenticated Step 1 Step 2 Step 3

Phone Authentication

Phone authentication is the most common method for end-user apps.

1

Initiate Authentication

Send phone number to request verification code

typescript
import De from '@de./sdk'

const access = new De.Access({
  workspace: 'your-workspace-id',
  env: 'prod'
})

// Request verification code
const response = await access.request({
  url: '/auth/signin',
  method: 'POST',
  body: {
    phone: '+1234567890',
    type: 'phone'
  }
})

console.log('Verification code sent!')
2

Verify Code

Submit the verification code received via SMS

typescript
const authResult = await access.request({
  url: '/auth/verify',
  method: 'POST',
  body: {
    phone: '+1234567890',
    code: '123456' // Code from SMS
  }
})

// Store the access token
const accessToken = authResult.data.token
localStorage.setItem('de_access_token', accessToken)

console.log('User authenticated:', authResult.data.user)
3

Use Access Token

Include token in subsequent requests

typescript
// Initialize with access token
const authenticatedAccess = new De.Access({
  workspace: 'your-workspace-id',
  accessToken: accessToken,
  env: 'prod'
})

// Now you can make authenticated requests
const userProfile = await authenticatedAccess.request({
  url: '/user',
  method: 'GET'
})

console.log('User profile:', userProfile.data)

Complete Phone Auth Example

React Example:

typescript
import { useState } from 'react'
import De from '@de./sdk'

export function PhoneAuth() {
  const [phone, setPhone] = useState('')
  const [code, setCode] = useState('')
  const [step, setStep] = useState<'phone' | 'code'>('phone')
  const [user, setUser] = useState(null)

  const access = new De.Access({
    workspace: process.env.REACT_APP_WORKSPACE_ID!,
    env: 'prod'
  })

  const requestCode = async () => {
    await access.request({
      url: '/auth/signin',
      method: 'POST',
      body: { phone, type: 'phone' }
    })
    setStep('code')
  }

  const verifyCode = async () => {
    const result = await access.request({
      url: '/auth/verify',
      method: 'POST',
      body: { phone, code }
    })
    
    localStorage.setItem('de_access_token', result.data.token)
    setUser(result.data.user)
  }

  if (user) {
    return <div>Welcome, {user.profile.firstName}!</div>
  }

  if (step === 'phone') {
    return (
      <div>
        <input
          type="tel"
          value={phone}
          onChange={(e) => setPhone(e.target.value)}
          placeholder="+1234567890"
        />
        <button onClick={requestCode}>Send Code</button>
      </div>
    )
  }

  return (
    <div>
      <input
        type="text"
        value={code}
        onChange={(e) => setCode(e.target.value)}
        placeholder="Enter 6-digit code"
      />
      <button onClick={verifyCode}>Verify</button>
    </div>
  )
}

Vue Example:

vue
<script setup lang="ts">
import { ref } from 'vue'
import De from '@de./sdk'

const phone = ref('')
const code = ref('')
const step = ref<'phone' | 'code'>('phone')
const user = ref(null)

const access = new De.Access({
  workspace: import.meta.env.VITE_WORKSPACE_ID,
  env: 'prod'
})

const requestCode = async () => {
  await access.request({
    url: '/auth/signin',
    method: 'POST',
    body: { phone: phone.value, type: 'phone' }
  })
  step.value = 'code'
}

const verifyCode = async () => {
  const result = await access.request({
    url: '/auth/verify',
    method: 'POST',
    body: { phone: phone.value, code: code.value }
  })
  
  localStorage.setItem('de_access_token', result.data.token)
  user.value = result.data.user
}
</script>

<template>
  <div v-if="user">
    Welcome, {{ user.profile.firstName }}!
  </div>
  <div v-else-if="step === 'phone'">
    <input v-model="phone" type="tel" placeholder="+1234567890" />
    <button @click="requestCode">Send Code</button>
  </div>
  <div v-else>
    <input v-model="code" type="text" placeholder="Enter 6-digit code" />
    <button @click="verifyCode">Verify</button>
  </div>
</template>

Vanilla JavaScript Example:

typescript
import De from '@de./sdk'

const access = new De.Access({
  workspace: 'your-workspace-id',
  env: 'prod'
})

let phone = ''
let code = ''

// Request verification code
document.getElementById('send-code-btn').addEventListener('click', async () => {
  phone = document.getElementById('phone-input').value
  
  await access.request({
    url: '/auth/signin',
    method: 'POST',
    body: { phone, type: 'phone' }
  })
  
  // Show code input
  document.getElementById('phone-step').style.display = 'none'
  document.getElementById('code-step').style.display = 'block'
})

// Verify code
document.getElementById('verify-btn').addEventListener('click', async () => {
  code = document.getElementById('code-input').value
  
  const result = await access.request({
    url: '/auth/verify',
    method: 'POST',
    body: { phone, code }
  })
  
  localStorage.setItem('de_access_token', result.data.token)
  
  // Show authenticated UI
  document.getElementById('user-name').textContent = result.data.user.profile.firstName
  document.getElementById('auth-steps').style.display = 'none'
  document.getElementById('authenticated').style.display = 'block'
})

Email/Password Authentication

For traditional email-based authentication.

1

Create Account

Register a new user with email and password

typescript
const createAccount = await access.request({
  url: '/auth/create',
  method: 'POST',
  body: {
    email: '[email protected]',
    password: 'SecurePassword123!',
    profile: {
      firstName: 'John',
      lastName: 'Doe'
    }
  }
})

console.log('Account created:', createAccount.data.user)
2

Sign In

Authenticate with email and password

typescript
const signIn = await access.request({
  url: '/auth/signin',
  method: 'POST',
  body: {
    email: '[email protected]',
    password: 'SecurePassword123!',
    type: 'email'
  }
})

const accessToken = signIn.data.token
localStorage.setItem('de_access_token', accessToken)

Password Requirements

Passwords must meet the following criteria:

  • Minimum 8 characters
  • At least one uppercase letter
  • At least one lowercase letter
  • At least one number
  • At least one special character

OAuth Authentication

Integrate with third-party OAuth providers.

Google OAuth

Sign in with Google for seamless authentication

Apple Sign In

Enable Apple Sign In for iOS users

1

Configure OAuth Provider

Set up OAuth credentials in your workspace

Navigate to your workspace dashboard and add OAuth credentials:

json
{
  "provider": "google",
  "clientId": "your-google-client-id",
  "clientSecret": "your-google-client-secret",
  "redirectUri": "https://yourapp.com/auth/callback"
}
2

Initiate OAuth Flow

Redirect user to OAuth provider

typescript
// Generate OAuth URL
const oauthUrl = `https://accounts.google.com/o/oauth2/v2/auth?` +
  `client_id=${clientId}&` +
  `redirect_uri=${redirectUri}&` +
  `response_type=code&` +
  `scope=openid email profile`

// Redirect user
window.location.href = oauthUrl
3

Handle OAuth Callback

Exchange authorization code for access token

typescript
// In your callback handler
const urlParams = new URLSearchParams(window.location.search)
const authCode = urlParams.get('code')

const result = await access.request({
  url: '/auth/oauth/callback',
  method: 'POST',
  body: {
    provider: 'google',
    code: authCode
  }
})

localStorage.setItem('de_access_token', result.data.token)
console.log('OAuth authentication successful:', result.data.user)

API Key Authentication

For server-to-server communication.

Use Cases for API Keys

API keys are ideal for:

  • Backend services
  • Scheduled jobs and cron tasks
  • Server-side integrations
  • Webhook handlers

Never use API keys in client-side code!

1

Generate API Key

Create an API key in your workspace dashboard

typescript
// Via API (requires admin access)
const apiKey = await access.request({
  url: '/workspace/api-keys',
  method: 'POST',
  body: {
    name: 'Production Server',
    scopes: ['orders:read', 'orders:write', 'fleet:read'],
    expiresAt: '2025-12-31'
  }
})

console.log('API Key:', apiKey.data.key)
// Store this securely - it won't be shown again!
2

Use API Key

Authenticate server requests with API key

typescript
// Server-side code
import De from '@de./sdk'

const serverAccess = new De.Access({
  workspace: process.env.DE_WORKSPACE_ID,
  accessToken: process.env.DE_API_KEY, // API key from environment
  env: 'prod'
})

// Make authenticated requests
const orders = await serverAccess.request({
  url: '/orders',
  method: 'GET',
  query: { status: 'pending' }
})

Token Management

Token Storage

Securely store access tokens in localStorage or cookies

Token Refresh

Automatically refresh tokens before expiration

Token Revocation

Revoke tokens when user signs out

Secure Token Storage

1

Store Tokens Securely

Best practices for token storage

typescript
// Web: Use httpOnly cookies for maximum security
document.cookie = `de_token=${accessToken}; Secure; HttpOnly; SameSite=Strict`

// Or localStorage for client-side access
localStorage.setItem('de_access_token', accessToken)

// Mobile: Use secure storage
import * as SecureStore from 'expo-secure-store'
await SecureStore.setItemAsync('de_access_token', accessToken)

Token Refresh

2

Implement Token Refresh

Refresh tokens before they expire

typescript
class TokenManager {
  private accessToken: string
  private refreshToken: string
  private expiresAt: number

  async refreshIfNeeded() {
    const now = Date.now()
    const expiresIn = this.expiresAt - now
    
    // Refresh if token expires in less than 5 minutes
    if (expiresIn < 5 * 60 * 1000) {
      await this.refresh()
    }
  }

  private async refresh() {
    const access = new De.Access({
      workspace: 'your-workspace-id',
      env: 'prod'
    })

    const result = await access.request({
      url: '/auth/refresh',
      method: 'POST',
      body: { refreshToken: this.refreshToken }
    })

    this.accessToken = result.data.token
    this.expiresAt = result.data.expiresAt
    
    localStorage.setItem('de_access_token', this.accessToken)
  }
}

Sign Out

3

Implement Sign Out

Revoke tokens and clear session

typescript
async function signOut() {
  const access = new De.Access({
    workspace: 'your-workspace-id',
    accessToken: localStorage.getItem('de_access_token'),
    env: 'prod'
  })

  // Revoke token on server
  await access.request({
    url: '/auth/signout',
    method: 'POST'
  })

  // Clear local storage
  localStorage.removeItem('de_access_token')
  localStorage.removeItem('de_user')

  // Redirect to login
  window.location.href = '/login'
}

User Profile Management

Once authenticated, you can manage user profiles.

1

Get User Profile

Fetch current user information

typescript
const profile = await access.request({
  url: '/user',
  method: 'GET'
})

console.log('User:', profile.data)
// {
//   uid: 'user_123',
//   profile: {
//     firstName: 'John',
//     lastName: 'Doe',
//     phone: '+1234567890',
//     email: '[email protected]',
//     photo: 'https://cdn.dedot.io/avatars/...'
//   },
//   account: {
//     types: ['customer'],
//     workspace: 'ws_abc'
//   }
// }
2

Update Profile

Modify user information

typescript
const updated = await access.request({
  url: '/user/profile',
  method: 'PATCH',
  body: {
    profile: {
      firstName: 'Jane',
      lastName: 'Smith'
    }
  }
})

console.log('Profile updated:', updated.data)
3

Upload Avatar

Update user profile photo

typescript
const formData = new FormData()
formData.append('file', avatarFile)

const upload = await access.request({
  url: '/utilities/upload',
  method: 'POST',
  body: formData,
  headers: {
    'Content-Type': 'multipart/form-data'
  }
})

console.log('Avatar uploaded:', upload.data.url)

Role-Based Access Control

De. supports role-based permissions for fine-grained access control.

User Roles & Permissions
Role
Permissions
Use Case
Owner
Full workspace control
Workspace administrator
Admin
User & resource management
Operations manager
Developer
API access & integration
Technical integration
Operator
Day-to-day operations
Dispatcher, warehouse staff
Viewer
Read-only access
Reporting, analytics

Check User Permissions

typescript
// Check if user has specific permission
const hasPermission = await access.request({
  url: '/workspace/access/check',
  method: 'POST',
  body: {
    permission: 'orders:write'
  }
})

if (hasPermission.data.allowed) {
  // User can create/update orders
}

Security Best Practices

Never Expose Credentials

  • Don't commit tokens to version control
  • Don't log tokens in console or errors
  • Don't send tokens in URL parameters
  • Use environment variables

Token Expiration

  • Set reasonable expiration times
  • Implement token refresh logic
  • Handle expired tokens gracefully
  • Clear tokens on sign out

HTTPS Only

  • Always use HTTPS in production
  • Enable secure cookie flags
  • Implement CORS properly
  • Use Content Security Policy

Multi-Factor Auth

  • Enable 2FA for sensitive operations
  • Use biometric authentication on mobile
  • Implement device verification
  • Monitor suspicious login attempts

Error Handling

1

Handle Authentication Errors

Properly handle and display auth errors

typescript
async function authenticate(phone: string, code: string) {
  try {
    const result = await access.request({
      url: '/auth/verify',
      method: 'POST',
      body: { phone, code }
    })
    
    return { success: true, data: result.data }
  } catch (error) {
    if (error.status === 401) {
      return { success: false, error: 'Invalid verification code' }
    } else if (error.status === 429) {
      return { success: false, error: 'Too many attempts. Try again later.' }
    } else if (error.status === 400) {
      return { success: false, error: 'Invalid phone number format' }
    }
    
    return { success: false, error: 'Authentication failed. Please try again.' }
  }
}

Next Steps

Authentication Complete - Ready to Build
User Authenticated
Access APIs
Build Features