stack/packages/stack-server/backend-design-doc.md
Zai Shi 96c26a7918
Magic link (#13)
* added magic link email, updated email template

* added magic link ui and db schema

* restructured sign in sign up page

* updated example custom button

* added joy tabs

* fixed bugs, added magic link errors, abstracted token creation

* added magic link callback

* fixed token bugs

* added more auth information to user object

* added changeset
2024-04-20 12:33:11 +02:00

9.2 KiB

Backend Design Doc

API format

  • RESTful
  • If X-Stack-Override-Error-Status header is given and the actual response status code is 400-599, the server MUST the status code with 200, and return the actual status code in the X-Stack-Actual-Status header. (This is useful to prevent unwanted console error logs generated by the browser.)
  • If a known error (see below) occurs, the X-Stack-Known-Error header MUST be returned containing the error code. The body MUST be of the shape { code: <ERROR_CODE>, message: <HUMAN_READABLE_ERROR_MESSAGE>, details?: {<ADDITIONAL_ERROR_INFORMATION>} }.
  • The X-Stack-Request-Id header MAY be returned with a randomly generated string that can be used to identify the request in the server logs. If an error occurs, the client SHOULD include this in the message as an error code.
  • The server may return a SchemaError if applicable, on any endpoint. This is referred to as "Common errors" in the endpoints list below.

Known errors

Terminology: "Invalid" means it was found but not valid, "Not found" means it was not found. "Error" could be anything. Make sure not to accidentally leak information, eg. "access forbidden" should often be "not found" or the user would know that the resource exists.

  • UnsupportedError: An error occured that is not currently supported (possibly because it was added in a version of Stack that is newer than this client). (400)
  • SchemaError: The request body does not match the expected schema. (400)
  • AllOverloadsFailed: This endpoint has multiple overloads, but they all failed to process the request. (400)
  • ProjectAuthenticationError:
    • InvalidProjectAuthentication:
      • InvalidPublishableClientKey: The publishable key is not valid for the given project. Does the project and/or the key exist? (401)
      • InvalidSecretServerKey: The secret server key is not valid for the given project. Does the project and/or the key exist? (401)
      • InvalidSuperSecretAdminKey: The super secret admin key is not valid for the given project. Does the project and/or the key exist? (401)
      • InvalidAdminAccessToken:
        • UnparsableAdminAccessToken: Admin access token is not parsable. (401)
        • AdminAccessTokenExpired: Admin access token has expired. Please refresh it and try again. (401)
        • InvalidProjectForAdminAccessToken: Admin access token not valid for this project. (401)
    • ProjectAuthenticationRequired:
      • ClientAuthenticationRequired: The publishable client key must be provided. (401)
      • ServerAuthenticationRequired: The secret server key must be provided. (401)
      • ClientOrServerAuthenticationRequired: Either the publishable client key or the secret server key must be provided. (401)
      • ClientOrAdminAuthenticationRequired: Either the publishable client key or the super secret admin key must be provided. (401)
      • ClientOrServerOrAdminAuthenticationRequired: Either the publishable client key, the secret server key, or the super secret admin key must be provided. (401)
      • AdminAuthenticationRequired: The super secret admin key must be provided. (401)
    • ExpectedInternalProject: The project ID is expected to be internal. (401)
  • SessionAuthenticationError:
    • InvalidSessionAuthentication:
      • InvalidAccessToken: 1
        • UnparsableAccessToken: Access token is not parsable. (401)
        • AccessTokenExpired: Access token has expired. Please refresh it and try again. (401)
        • InvalidProjectForAccessToken: Access token not valid for this project. (401)
      • SessionUserEmailNotVerified: User e-mail not verified, but is required by the project. (401)
    • SessionAuthenticationRequired: Session required for this request. (401)
  • RefreshTokenError:
    • ProviderRejected: The provider refused to refresh their token. (401)
    • InvalidRefreshToken: Refresh token is invalid, possibly because it has expired. A new refresh token requires reauthentication. (401)
  • UserEmailAlreadyExists: User already exists. (400)
  • UserNotFound: User not found. (404)
  • ApiKeyNotFound: API key not found. (404)
  • ProjectNotFound: Project not found or is not accessible. (404)
  • EmailPasswordMismatch: Wrong e-mail or password. (400)
  • RedirectUrlNotWhitelisted: The redirect URL is not whitelisted. (400)
  • PasswordRequirementsNotMet:
    • PasswordTooShort: Password too short. (400)
    • PasswordTooLong: Password too long. (400)
  • EmailVerificationError:
    • EmailVerificationCodeError:
      • EmailVerificationCodeNotFound: The e-mail verification code does not exist for this project. (404)
      • EmailVerificationCodeExpired: The e-mail verification code has expired. (400)
      • EmailVerificationCodeAlreadyUsed: The e-mail verification code has already been used. (400)
  • MagicLinkError:
    • MagicLinkCodeError:
      • MagicLinkCodeNotFound: The magic link code does not exist for this project. (404)
      • MagicLinkCodeExpired: The magic link code has expired. (400)
      • MagicLinkCodeAlreadyUsed: The magic link code has already been used. (400)
  • PasswordResetError:
    • PasswordResetCodeError:
      • PasswordResetCodeNotFound: The password reset code does not exist for this project. (404)
      • PasswordResetCodeExpired: The password reset code has expired. (400)
      • PasswordResetCodeAlreadyUsed: The password reset code has already been used. (400)
  • PasswordMismatch: The given password does not match the user's password. (400)

Project Authentication

  • On endpoints that require project authentication, the client MUST provide:
    • The project ID in the X-Stack-Project-Id header.
    • One of:
      • The publishable client key in the X-Stack-Publishable-Client-Key header.
      • The secret server key in the X-Stack-Secret-Server-Key header.
      • The super secret admin key in the X-Stack-Super-Secret-Admin-Key header.
      • The admin access token in the X-Stack-Admin-Access-Token header.
  • The server MUST respond with a InvalidProjectAuthentication error if there is an authentication error on the server.

Session Authentication

  • On endpoints that require session authentication, the client MUST provide:
    • Project authentication with client permissions.
    • The access token in the X-Stack-Access-Token header.
  • The server MUST respond with a InvalidSessionAuthentication error if there is an authentication error on the server.

Endpoints

  • /api-keys
    • Overload 1: List or create API keys for the project.
      • Methods: GET, POST
      • Errors: Common errors, InvalidProjectAuthentication, AdminAuthenticationRequired.
  • /api-keys/:id
    • Overload 1: Get, update, or delete the API key with the given ID.
      • Methods: GET, PATCH, DELETE
      • Errors: Common errors, InvalidProjectAuthentication, AdminAuthenticationRequired, ApiKeyNotFound.
  • /auth/...
    • not part of the rework for now
  • /current-project
    • Overload 1: Get, update, or delete the current project admin.
      • Methods: GET, PATCH, DELETE
      • Errors: Common errors, InvalidProjectAuthentication, AdminAuthenticationRequired.
    • Overload 2: Get the current client project.
      • Methods: GET
      • Errors: Common errors, InvalidProjectAuthentication, ClientAuthenticationRequired.
  • /current-user
    • Overload 1: Get or update the current user.
      • Methods: GET, PATCH
      • Errors: Common errors, InvalidProjectAuthentication, ClientAuthenticationRequired, SessionAuthenticationRequired.
    • Overload 2: Get or update the current server user.
      • Methods: GET, PATCH
      • Errors: Common errors, InvalidProjectAuthentication, ServerAuthenticationRequired, SessionAuthenticationRequired.
  • /projects
    • Overload 1: List or create projects for the current user.
      • Methods: GET, POST
      • Errors: Common errors, InvalidProjectAuthentication, ClientAuthenticationRequired, ExpectedInternalProject, SessionAuthenticationRequired.
  • /users
    • Overload 1: List users in the project.
      • Methods: GET
      • Errors: Common errors, InvalidProjectAuthentication, ServerAuthenticationRequired.
  • /users/:id
    • Overload 1: Get, update, or delete the user with the given ID.
      • Methods: GET, PATCH, DELETE
      • Errors: Common errors, InvalidProjectAuthentication, ServerAuthenticationRequired, UserNotFound.

Example endpoint implementation

const requestBase = {};

const handler = smartRouteHandler(
  [{ isClient: true }, { isClient: false }],
  ({ isClient }) => {
    request: yup.object({
      project: isClient ? requireClientAuthentication : requireServerAuthentication,
      params: yup.object({
        id: yup.string().required(),
      }),
    }),
    response: yup.object({
      status: 200,
      body: yup.object({
        id: yup.string().required(),
        name: yup.string().required(),
        ...isClient ? {} : { secret: yup.string().required() },
      }),
    }),
    handler: (obj) => {
      return {
        status: 200,
        body: {
          id: obj.params.id,
          name: "John Doe",
          ...isClient ? {} : { secret: "super-secret" },
        },
      };
    },
  },
);

export const GET = handler;

Footnotes


  1. The client SHOULD remove the access token from the client storage, and request a new one using the refresh token. ↩︎