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:
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
Always Verify
Always verify user identity in protected functions - never trust client-provided user IDs.
Handle Errors
Handle NoUserIdentityFoundError explicitly to provide clear error messages.
Store User Data
Create user profiles in your database to store additional user information.
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
HTTP API Authenticate HTTP requests
Convex Auth Set up Convex authentication