Skip to main content

Overview

The @repo/dally package provides authentication utilities, environment configuration, and API helpers for Openlane applications. It centralizes configuration management and provides type-safe functions for user authentication and AI features.

Installation

pnpm add @repo/dally

Package Exports

ExportPathDescription
auth@repo/dally/authEnvironment config and API URLs
user@repo/dally/userUser authentication functions
chat@repo/dally/chatChat configuration
ai@repo/dally/aiAI/LLM configuration

Authentication Configuration

API URLs and Configuration

import { 
  openlaneAPIUrl,
  openlaneGQLUrl,
  websocketGQLUrl,
  sessionCookieName,
  sessionCookieDomain,
  cookieDomain,
  csrfCookieName,
  csrfHeader,
  siteUrl
} from '@repo/dally/auth'

// API base URL
const apiUrl = openlaneAPIUrl // process.env.NEXT_PUBLIC_OPENLANE_URL

// GraphQL endpoint
const gqlUrl = openlaneGQLUrl // ${NEXT_PUBLIC_OPENLANE_URL}/query

// WebSocket GraphQL URL
const wsUrl = websocketGQLUrl // wss://api.example.com/query

// Session configuration
const sessionName = sessionCookieName
const sessionDomain = sessionCookieDomain

// CSRF protection
const csrfCookie = csrfCookieName // 'ol.csrf-token'
const csrfHeaderName = csrfHeader // 'X-CSRF-Token'

Environment Detection

import { 
  isDevelopment,
  isVercelDev 
} from '@repo/dally/auth'

if (isDevelopment) {
  console.log('Running in development mode')
}

if (isVercelDev) {
  console.log('Running on Vercel preview/dev environment')
}

Security Configuration

import { 
  recaptchaSiteKey,
  allowedLoginDomains 
} from '@repo/dally/auth'

// reCAPTCHA integration
function LoginForm() {
  return (
    <ReCAPTCHA
      sitekey={recaptchaSiteKey}
      onChange={handleCaptchaChange}
    />
  )
}

// Domain restrictions
const isAllowedDomain = allowedLoginDomains.includes(emailDomain)

Feature Flags

import { 
  includeQuestionnaireCreation,
  enableDevrevChat,
  cname 
} from '@repo/dally/auth'

// Conditional feature rendering
{includeQuestionnaireCreation && (
  <Button>Create Questionnaire</Button>
)}

// Chat integration
{enableDevrevChat && (
  <ChatWidget appId={chatAppId} />
)}

// Custom domain
const customDomain = cname

Third-party Integration

import { 
  chatAppId,
  surveyLicenseKey,
  pirschAnalyticsKey,
  stripeSecretKey 
} from '@repo/dally/auth'

// Chat application
const chatId = chatAppId // NEXT_PUBLIC_CHAT_APP_ID

// Survey.js license
const surveyKey = surveyLicenseKey // NEXT_PUBLIC_SURVEYJS_KEY

// Analytics
const analyticsKey = pirschAnalyticsKey // NEXT_PUBLIC_PIRSCH_KEY

// Payment processing (server-side only)
const stripeKey = stripeSecretKey

User Authentication

User Registration

import { registerUser, type RegisterUser } from '@repo/dally/user'

async function handleRegister(data: RegisterUser) {
  const result = await registerUser({
    username: data.username,
    password: data.password,
    confirmedPassword: data.confirmedPassword,
  })

  if (result.message) {
    console.error('Registration failed:', result.message)
  } else {
    console.log('Registration successful:', result)
  }
}

// Example component
function SignupForm() {
  const [formData, setFormData] = useState<RegisterUser>({
    username: '',
    password: '',
    confirmedPassword: ''
  })

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()
    await handleRegister(formData)
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={formData.username}
        onChange={(e) => setFormData({ ...formData, username: e.target.value })}
        placeholder="Email"
      />
      <input
        type="password"
        value={formData.password}
        onChange={(e) => setFormData({ ...formData, password: e.target.value })}
        placeholder="Password"
      />
      <input
        type="password"
        value={formData.confirmedPassword}
        onChange={(e) => setFormData({ ...formData, confirmedPassword: e.target.value })}
        placeholder="Confirm Password"
      />
      <button type="submit">Sign Up</button>
    </form>
  )
}

User Verification

import { verifyUser } from '@repo/dally/user'

async function handleEmailVerification(token: string) {
  const result = await verifyUser(token)

  if (result.message) {
    console.error('Verification failed:', result.message)
  } else {
    console.log('Email verified successfully:', result)
  }
}

// Example usage in a verification page
function VerifyEmailPage() {
  const searchParams = useSearchParams()
  const token = searchParams.get('token')

  useEffect(() => {
    if (token) {
      handleEmailVerification(token)
    }
  }, [token])

  return <div>Verifying your email...</div>
}

User Login Types

import { type LoginUser } from '@repo/dally/user'

const credentials: LoginUser = {
  username: '[email protected]',
  password: 'secure-password'
}

// Use with your authentication flow
function LoginForm() {
  const [loginData, setLoginData] = useState<LoginUser>({
    username: '',
    password: ''
  })

  // Handle login with your auth provider
}

Chat Configuration

import { 
  bedrockModelArn,
  enableChat 
} from '@repo/dally/chat'

// Check if chat is enabled
if (enableChat === 'true') {
  // Initialize chat features
}

// AWS Bedrock model configuration
const modelArn = bedrockModelArn

AI Configuration

AI Feature Detection

import { aiEnabled } from '@repo/dally/ai'

function PolicyEditor() {
  return (
    <div>
      <textarea />
      {aiEnabled && (
        <Button>Generate with AI</Button>
      )}
    </div>
  )
}

Google Vertex AI Configuration

import { 
  googleAPIKey,
  googleProjectID,
  googleAIRegion,
  aiLogBucket,
  ragCorpusID,
  geminiModelName 
} from '@repo/dally/ai'

// Vertex AI client configuration
const vertexConfig = {
  apiKey: googleAPIKey,
  projectId: googleProjectID,
  region: googleAIRegion,
  logBucket: aiLogBucket,
  ragCorpus: ragCorpusID
}

// Model selection
const modelName = geminiModelName // Default: 'gemini-2.5-flash'

AI Generation Parameters

import { 
  aiSystemInstruction,
  controlSystemInstruction,
  policyPrompt,
  temperature,
  maxOutputTokens 
} from '@repo/dally/ai'

// System instructions
const systemPrompt = aiSystemInstruction
// "You are a helpful assistant that provides concise and accurate answers..."

const controlPrompt = controlSystemInstruction
// "When referencing controls, always use their ref codes..."

// Generate policy prompt
const prompt = policyPrompt('Data Privacy Policy')
// "Generate a formal policy document for 'Data Privacy Policy'..."

// Model parameters
const modelConfig = {
  temperature: temperature,        // Default: 0.1
  maxOutputTokens: maxOutputTokens // Default: 5000
}

AI Integration Example

import { 
  aiEnabled,
  policyPrompt,
  temperature,
  maxOutputTokens 
} from '@repo/dally/ai'

function PolicyGenerator() {
  const [policyName, setPolicyName] = useState('')
  const [generatedContent, setGeneratedContent] = useState('')

  const generatePolicy = async () => {
    if (!aiEnabled) {
      console.error('AI features are not enabled')
      return
    }

    const prompt = policyPrompt(policyName)
    
    // Call your AI generation endpoint
    const response = await fetch('/api/generate-policy', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        prompt,
        temperature,
        maxOutputTokens
      })
    })

    const data = await response.json()
    setGeneratedContent(data.content)
  }

  return (
    <div>
      <input
        value={policyName}
        onChange={(e) => setPolicyName(e.target.value)}
        placeholder="Policy name"
      />
      {aiEnabled && (
        <Button onClick={generatePolicy}>Generate with AI</Button>
      )}
      <div>{generatedContent}</div>
    </div>
  )
}

Common Patterns

WebSocket Connection

import { websocketGQLUrl } from '@repo/dally/auth'
import { createClient } from 'graphql-ws'

const wsClient = createClient({
  url: websocketGQLUrl,
  connectionParams: {
    // Authentication headers
  }
})

API Client Configuration

import { openlaneGQLUrl, csrfHeader, csrfCookieName } from '@repo/dally/auth'
import { GraphQLClient } from 'graphql-request'

const client = new GraphQLClient(openlaneGQLUrl, {
  headers: {
    [csrfHeader]: getCSRFToken(),
  },
  credentials: 'include',
})

function getCSRFToken() {
  return document.cookie
    .split('; ')
    .find(row => row.startsWith(`${csrfCookieName}=`))
    ?.split('=')[1]
}

Protected Route Check

import { sessionCookieName } from '@repo/dally/auth'
import { cookies } from 'next/headers'

export async function middleware(request: Request) {
  const cookieStore = cookies()
  const session = cookieStore.get(sessionCookieName)

  if (!session) {
    return Response.redirect(new URL('/login', request.url))
  }
}

Environment Variables

The package expects these environment variables:

Authentication

  • NEXT_PUBLIC_OPENLANE_URL - Openlane API base URL
  • SESSION_COOKIE_NAME - Session cookie name
  • SESSION_COOKIE_DOMAIN - Session cookie domain
  • COOKIE_DOMAIN - General cookie domain
  • NEXT_PUBLIC_CSRF_COOKIE_NAME - CSRF token cookie
  • NEXT_PUBLIC_CSRF_HEADER - CSRF header name

Features

  • NEXT_PUBLIC_INCLUDE_QUESTIONNAIRE_CREATION - Enable questionnaire features
  • NEXT_PUBLIC_ENABLE_CHATBOT - Enable chat features
  • NEXT_PUBLIC_ENABLE_DEVREV_CHAT - Enable DevRev chat
  • NEXT_PUBLIC_AI_SUGGESTIONS_ENABLED - Enable AI features

Third-party

  • NEXT_PUBLIC_RECAPTCHA_SITE_KEY - reCAPTCHA site key
  • NEXT_PUBLIC_CHAT_APP_ID - Chat application ID
  • NEXT_PUBLIC_SURVEYJS_KEY - Survey.js license key
  • NEXT_PUBLIC_PIRSCH_KEY - Pirsch analytics key
  • STRIPE_SECRET_KEY - Stripe secret key (server-side)

AI Configuration

  • GOOGLE_SERVICE_ACCOUNT_KEY_B64 - Google service account key
  • GOOGLE_AI_PROJECT_ID - Google AI project ID
  • GOOGLE_AI_REGION - Google AI region
  • GOOGLE_AI_MODEL_NAME - Gemini model name
  • BEDROCK_MODEL_ARN - AWS Bedrock model ARN

Best Practices

  1. Centralize configuration - Import from @repo/dally instead of accessing process.env directly
  2. Type safety - Use exported types like LoginUser and RegisterUser
  3. Error handling - Always check for .message property in API responses
  4. Feature flags - Check configuration before rendering features
  5. Server vs Client - Be aware of which values are public vs server-only

Security Considerations

  • CSRF tokens are included in API requests
  • Session cookies use secure domains
  • reCAPTCHA prevents automated abuse
  • Environment variables separate public and private config
  • Email domain restrictions can be enforced

Build docs developers (and LLMs) love