Skip to content

dimensionalpocket/dps-auth-api-rs

Repository files navigation

Dimensional Pocket - Auth Service

This document outlines the project configuration and roadmap for the Rust-based GraphQL Auth Service for Dimensional Pocket. The service will provide REST endpoints, a GraphQL API, and user management functionalities.

Project Configuration

  • Project Name: dp-auth-service
  • Indentation: Use 2 spaces for Rust code (not the standard 4 spaces)
  • GraphQL Library: async-graphql
  • Database: sqlx with SQLite on a file in the repo root's data directory (will be mounted in production)
  • REST endpoints: / and /health, via axum
  • GraphQL Endpoint: /graphql
  • Async Runtime: tokio
  • Password Hashing: argon2; 16-byte salt generated with rand::rngs::OsRng
  • Design Patterns:
    • GraphQL queries and mutations only call Service objects and handle the response
      • E.g., getServerTimestamp query calls ServerService::get_server_timestamp
      • Service objects are static and do not require instantiation
      • Service objects have their own unit tests with full coverage
      • GraphQL Query and Mutation tests only test if they're calling the Service methods with the correct parameters
      • Service objects are stored in src/services
      • Each GraphQL query and mutation are implemented in independent files in src/graphql/queries and src/graphql/mutations respectively
        • Files are named directly after the query or mutation they implement, e.g., get_server_timestamp.rs for the getServerTimestamp query
        • The structs for the queries and mutations are suffixed with Query or Mutation
          • E.g., GetServerTimestampQuery for the getServerTimestamp query
    • SQL Queries are executed via SQL Query objects
      • E.g., UserByIdQuery::run(user_id)
      • All SQL query objects have a run method (arguments may vary) that executes the database query and returns the result
      • SQL Query objects are static and do not require instantiation
      • SQL Query objects are stored in src/queries

Roadmap

Phase 1: Project Setup w/ REST Endpoints + getServerTimestamp query

  • Initialize Rust project
  • Configure async-graphql, axum, sqlx, and tokio
  • Create REST endpoints:
    • / - Returns a simple "OK" message
    • /health - Returns a simple "OK" message
  • Implement getServerTimestamp GraphQL query and associated Service object:
    • Returns the current server timestamp (milliseconds since epoch)
  • Test the REST endpoints, getServerTimestamp query, and the Service object
  • Document the API endpoints and query

Phase 2: Logging

  • Suggest industry standards for logging endpoints and queries in Rust/GraphQL
  • Implement logging for existing REST endpoints and GraphQL queries (check Phase 1 for a list of endpoints and queries)
    • For REST endpoints, log the request method, path, response status, and response time
    • For GraphQL queries, log the operation name and response time (no parameters)

Phase 3: Password Service

  • Implement PasswordService for user password management
    • generate method to create a new password hash
      • Already includes the salt generation
    • verify method to check a password against a hash
  • Test the PasswordService methods
  • Document the PasswordService methods via Rust doc comments

Phase 4: Database Configuration

  • Configure sqlx to use SQLite database in data/development.db (filename to come from environment variable)
  • Create database schema with sqlx migrations
    • Create migration for user_roles table
    • Create migration for users table
    • Schema defined with proper foreign key constraints
    • Migrations stored in config/database/migrations
  • Run migrations to create the database schema
  • Verify support for schema dump after running migrations, to be stored in config/database/schema.sql
  • Implement SQL Query objects following project patterns
  • Create comprehensive unit tests for all query objects
  • Create integration tests demonstrating complete user creation flow
  • Implement seeds system with default user roles

Phase 5: User Roles and Permissions

  • Suggest industry standards for user roles and permissions in Rust/GraphQL
  • Permissions are granular (e.g., can_create_user, can_delete_user, etc.) and can be assigned to roles
  • Update the database schema to store permissions for roles (details to be defined)
  • Implement UserRoleService for managing user roles and checking permissions
    • get_role_by_id method to retrieve a role by ID
    • get_role_by_name method to retrieve a role by name
    • check_permission method to check if a user has a specific permission

Phase 6: User Service - User Creation and Retrieval

  • Implement UserService for user management
    • create_user method to create a new user
      • Validates input (username and password) and checks for username already in use
      • Uses PasswordService to hash the password
      • Assigns default role to the user
    • get_user_by_id method to retrieve a user by ID
    • get_user_by_name method to retrieve a user by name

Phase 7: createUser Mutation

  • Implement createUser GraphQL mutation
    • Calls UserService::create_user
    • Returns the created user object
  • Unit tests for the createUser mutation
    • Tests should verify that the mutation calls the UserService::create_user method with the correct parameters
  • Integration tests for the createUser mutation
    • Call the endpoint to create a user

Phase 8: Session Service - Token Management

  • Implement SessionService for managing session tokens
    • We're using a custom encrypted token format, not JWT
      • Why not JWT? Because we don't want to expose the payload structure to consumers
      • Consumers will call a future getCurrentSession query to retrieve the session payload with the token in the request (header or cookie)
    • Define the payload structure for the session token - JSON-serializable
      • Fields: sub (subject - user id), iat (issued at timestamp), exp (expiration timestamp)
    • Requires a secret key for signing tokens, stored in an environment variable (DP_AUTH_SECRET_KEY)
      • Determine how the service will act if the secret key is not set
    • encode_token method to create a session token from a payload
    • decode_token method to decode a session token and retrieve the payload
  • Test the SessionService methods
  • Document the SessionService methods via Rust doc comments

Phase 9: Session Middleware

  • Implement middleware for session token management, to be used in the GraphQL API only (all queries and mutations)
  • Middleware should:
    • Check for the session token in the request header or cookie, in that order (if both present, prefer the header)
      • Do not fallback to cookie if the header token is present but invalid
    • Decode the token using SessionService::decode_token
    • If valid, attach the decoded session token payload to the request context
    • If invalid or missing, don't attach the payload and allow the request to proceed without it (each resolver will handle the absence of the payload)
  • Decisions to make:
    • What is the prefix for the auth header, considering it's a custom encrypted token? ("Bearer" or something else?)
    • Do we need to know the cookie name? If so, use DpAuthSession as the cookie name and store this string in a constant
  • Allow resolvers to have access to the session token payload via the request context
    • Resolvers can then propagate the session token payload to the Service objects on a case-by-case basis
  • Unit tests to ensure the middleware is attaching the session token payload when valid
    • Integration tests will be implemented in a later phase, by resolvers that actually use the session token payload

Phase 10: SessionService::create_session Method

  • Accepts username and password as input
  • Calls UserService::get_user_by_name to retrieve the user by username
  • Calls PasswordService::verify to check the password against the stored hash
  • If valid, calls SessionService::encode_token to create a session token
  • Returns the session token, or an error if the credentials are invalid
    • Error message should be specific (e.g., "User is blank" or "User not found" or "Password is blank" or "Password does not match", etc)
    • Errors should be logged using the existing logging system and must contain the given username (not the password) for debugging
    • Errors should be logged at info level, not warn or error -- those errors should not raise alarms in our logs, they're just infomational
  • Verify existing CORS configuration for 'with_credentials' support
  • Unit tests for the create_session method
  • Document the create_session method via Rust doc comments

Phase 11: createSession Mutation (sign-in)

  • Implement createSession GraphQL mutation
    • Accepts username and password as input
    • Calls SessionService::create_session
      • On success, returns the session token and sets the cookie with the token in the response
      • On failure, returns a user-friendly error message (not the internal error message), e.g., "Invalid credentials" for any username or password error, or "Internal server error" for unexpected errors
    • Make a decision if errors should return 2XX or 4XX status codes, as it impacts the client-side error handling (decision: 2XX)
  • Unit tests for the createSession mutation, ensuring it calls the SessionService::create_session method with the correct parameters
  • Integration tests for the createSession mutation, ensuring it returns a session token and sets the cookie in the response

Phase 12: getCurrentSession Query

  • Returns the current session token payload, set by the session middleware
  • Make a decision if the query should return 2XX or 401 status code if the session token is not present or invalid
  • Unit tests for the getCurrentSession query
  • Integration tests for the getCurrentSession query

Phase 13: Handle 404s in the REST API

  • Implement a 404 handler for the REST API
  • Requests to non-existent endpoints should return a 404 status code with a plain "NOT FOUND" message
  • Requests should be logged with the request method, path, querystring, and body size (if present); log level should be info
  • Integration tests for the 404 handler

Future Phases

  • Email support
  • Cookie-less session management (using custom headers in response)
  • Password change (when logged in)
  • Password reset (requires email support)
  • Username change
  • User deletion/redaction
  • user_sessions table to track user sessions and make tokens revocable

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages