Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/rjdellecese/confect/llms.txt

Use this file to discover all available pages before exploring further.

The Auth service provides access to user authentication and identity information in your Confect functions. Built on Convex’s authentication system, it integrates seamlessly with Effect for type-safe auth handling.

Overview

User Identity

Get authenticated user information

Effect Integration

Type-safe error handling for auth failures

OAuth Support

Works with Convex OAuth providers

Custom Auth

Implement custom authentication logic

Auth Service

The Auth service is available in queries, mutations, and actions:
import { Auth } from "@confect/server";
import { Effect } from "effect";

const getCurrentUser = Effect.gen(function* () {
  const auth = yield* Auth;
  
  const identity = yield* auth.getUserIdentity;
  
  return {
    userId: identity.subject,
    email: identity.email,
    name: identity.name,
  };
});

Getting User Identity

The getUserIdentity effect returns the authenticated user’s identity:
import { Auth } from "@confect/server";
import { Effect } from "effect";

const getUserInfo = Effect.gen(function* () {
  const auth = yield* Auth;
  
  const identity = yield* auth.getUserIdentity;
  
  // Identity contains:
  // - subject: unique user ID
  // - email: user's email
  // - name: user's name
  // - tokenIdentifier: token for this session
  
  return identity;
});
getUserIdentity returns an Effect that fails with NoUserIdentityFoundError if the user is not authenticated.

Handling Unauthenticated Users

Handle authentication failures gracefully:
import { Auth, NoUserIdentityFoundError } from "@confect/server";
import { Effect } from "effect";

const getProfile = Effect.gen(function* () {
  const auth = yield* Auth;
  
  const identity = yield* auth.getUserIdentity.pipe(
    Effect.catchTag("NoUserIdentityFoundError", () =>
      Effect.fail(new Error("Please sign in to view your profile"))
    )
  );
  
  return identity;
});
Or return a default value:
const getOptionalUser = Effect.gen(function* () {
  const auth = yield* Auth;
  
  const identity = yield* auth.getUserIdentity.pipe(
    Effect.catchTag("NoUserIdentityFoundError", () =>
      Effect.succeed(null)
    )
  );
  
  return identity;
});

Protecting Functions

Require authentication in your functions:
import { FunctionImpl, Auth, DatabaseWriter } from "@confect/server";
import { Effect } from "effect";

const createPost = FunctionImpl.make(
  api,
  "posts",
  "create",
  ({ title, content }) =>
    Effect.gen(function* () {
      const auth = yield* Auth;
      const db = yield* DatabaseWriter<typeof databaseSchema>();
      
      // Require authentication
      const identity = yield* auth.getUserIdentity;
      
      const postId = yield* db.table("posts").insert({
        title,
        content,
        authorId: identity.subject,
        createdAt: Date.now(),
      });
      
      return { postId };
    }).pipe(Effect.orDie)
);
If a user tries to call a protected function without authentication, they’ll receive a NoUserIdentityFoundError.

Role-Based Access Control

Implement role-based authorization:
import { Schema } from "effect";

class UnauthorizedError extends Schema.TaggedError<UnauthorizedError>()()
  "UnauthorizedError",
  { message: Schema.String }
) {}

const requireAdmin = Effect.gen(function* () {
  const auth = yield* Auth;
  const db = yield* DatabaseReader<typeof databaseSchema>();
  
  const identity = yield* auth.getUserIdentity;
  
  const user = yield* db.table("users").get(
    "by_subject",
    identity.subject
  );
  
  if (user.role !== "admin") {
    return yield* Effect.fail(
      new UnauthorizedError({ message: "Admin access required" })
    );
  }
  
  return user;
});

const deleteUser = FunctionImpl.make(
  api,
  "admin",
  "deleteUser",
  ({ userId }) =>
    Effect.gen(function* () {
      // Verify admin
      yield* requireAdmin;
      
      const db = yield* DatabaseWriter<typeof databaseSchema>();
      
      yield* db.table("users").delete(userId);
    }).pipe(Effect.orDie)
);

User Management

Create user profiles after authentication:
const getOrCreateUser = Effect.gen(function* () {
  const auth = yield* Auth;
  const db = yield* DatabaseReader<typeof databaseSchema>();
  const dbWrite = yield* DatabaseWriter<typeof databaseSchema>();
  
  const identity = yield* auth.getUserIdentity;
  
  // Check if user exists
  const existingUser = yield* db
    .table("users")
    .get("by_subject", identity.subject)
    .pipe(
      Effect.catchTag("GetByIndexFailure", () => Effect.succeed(null))
    );
  
  if (existingUser) {
    return existingUser;
  }
  
  // Create new user
  const userId = yield* dbWrite.table("users").insert({
    subject: identity.subject,
    email: identity.email,
    name: identity.name,
    role: "user",
    createdAt: Date.now(),
  });
  
  const newUser = yield* db.table("users").get(userId);
  return newUser;
});

Access Control Patterns

Owner-Only Access

const updatePost = FunctionImpl.make(
  api,
  "posts",
  "update",
  ({ postId, title, content }) =>
    Effect.gen(function* () {
      const auth = yield* Auth;
      const db = yield* DatabaseReader<typeof databaseSchema>();
      const dbWrite = yield* DatabaseWriter<typeof databaseSchema>();
      
      const identity = yield* auth.getUserIdentity;
      
      // Check ownership
      const post = yield* db.table("posts").get(postId);
      
      if (post.authorId !== identity.subject) {
        return yield* Effect.fail(
          new UnauthorizedError({ message: "Not the post owner" })
        );
      }
      
      // Update post
      yield* dbWrite.table("posts").patch(postId, {
        title,
        content,
        updatedAt: Date.now(),
      });
    }).pipe(Effect.orDie)
);

Team-Based Access

const requireTeamMember = (teamId: string) =>
  Effect.gen(function* () {
    const auth = yield* Auth;
    const db = yield* DatabaseReader<typeof databaseSchema>();
    
    const identity = yield* auth.getUserIdentity;
    
    const membership = yield* db
      .table("team_members")
      .get("by_team_and_user", teamId, identity.subject)
      .pipe(
        Effect.catchTag("GetByIndexFailure", () =>
          Effect.fail(
            new UnauthorizedError({ message: "Not a team member" })
          )
        )
      );
    
    return membership;
  });

const createTeamPost = FunctionImpl.make(
  api,
  "teams",
  "createPost",
  ({ teamId, title, content }) =>
    Effect.gen(function* () {
      const auth = yield* Auth;
      const db = yield* DatabaseWriter<typeof databaseSchema>();
      
      // Verify team membership
      yield* requireTeamMember(teamId);
      
      const identity = yield* auth.getUserIdentity;
      
      const postId = yield* db.table("posts").insert({
        teamId,
        title,
        content,
        authorId: identity.subject,
        createdAt: Date.now(),
      });
      
      return { postId };
    }).pipe(Effect.orDie)
);

Custom Authentication

Implement custom authentication with API keys:
import { ActionCtx } from "@confect/server";
import { Effect } from "effect";

const verifyAPIKey = (apiKey: string) =>
  Effect.gen(function* () {
    const db = yield* DatabaseReader<typeof databaseSchema>();
    
    // Hash the API key
    const hashedKey = hashAPIKey(apiKey);
    
    // Look up key
    const keyRecord = yield* db
      .table("apiKeys")
      .get("by_hash", hashedKey)
      .pipe(
        Effect.catchTag("GetByIndexFailure", () =>
          Effect.fail(new Error("Invalid API key"))
        )
      );
    
    return keyRecord.userId;
  });

const apiEndpoint = FunctionImpl.make(
  api,
  "api",
  "data",
  ({ apiKey }) =>
    Effect.gen(function* () {
      const db = yield* DatabaseReader<typeof databaseSchema>();
      
      // Verify API key
      const userId = yield* verifyAPIKey(apiKey);
      
      // Return user's data
      const data = yield* db
        .table("data")
        .index("by_user", (q) => q.eq("userId", userId))
        .collect();
      
      return data;
    }).pipe(Effect.orDie)
);

HTTP API Authentication

Authenticate HTTP requests:
import { HttpApiBuilder, HttpServerRequest } from "@effect/platform";
import { Effect } from "effect";

const authMiddleware = (app: HttpApp.Default) =>
  HttpMiddleware.make((request) =>
    Effect.gen(function* () {
      const req = yield* HttpServerRequest.HttpServerRequest;
      const auth = yield* Auth;
      
      // Check if user is authenticated
      const identity = yield* auth.getUserIdentity.pipe(
        Effect.catchTag("NoUserIdentityFoundError", () =>
          Effect.fail(
            HttpServerResponse.unauthorized(
              Schema.Struct({ error: Schema.Literal("Unauthorized") })
            )
          )
        )
      );
      
      // Continue with authenticated request
      return yield* app(request);
    })
  );

User Schema

Define a user table with authentication fields:
schema.ts
import { Table } from "@confect/server";
import { Schema } from "effect";

const usersTable = Table.make(
  "users",
  Schema.Struct({
    subject: Schema.String, // Convex user ID
    email: Schema.String,
    name: Schema.String,
    role: Schema.Literal("user", "admin", "moderator"),
    createdAt: Schema.Number,
    lastLoginAt: Schema.Number,
  })
)
  .index("by_subject", ["subject"])
  .index("by_email", ["email"]);

Best Practices

1

Always Verify

Always verify user identity in protected functions - never trust client-provided user IDs.
2

Handle Errors

Handle NoUserIdentityFoundError explicitly to provide clear error messages.
3

Store User Data

Create user profiles in your database to store additional user information.
4

Implement RBAC

Use role-based access control for complex authorization requirements.
Never expose the user’s tokenIdentifier to other users - it’s sensitive authentication information.
Convex handles authentication tokens automatically. You don’t need to manage JWTs or sessions manually.

Next Steps

Functions

Learn about queries and mutations

Database

Store user data

HTTP API

Authenticate HTTP requests

Convex Auth

Set up Convex authentication