1469 lines
53 KiB
Go
1469 lines
53 KiB
Go
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log/slog"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/bradfitz/gomemcache/memcache"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/nhost/nhost/internal/lib/oapi"
|
|
oapimw "github.com/nhost/nhost/internal/lib/oapi/middleware"
|
|
"github.com/nhost/nhost/services/auth/docs"
|
|
"github.com/nhost/nhost/services/auth/go/api"
|
|
"github.com/nhost/nhost/services/auth/go/controller"
|
|
crypto "github.com/nhost/nhost/services/auth/go/cryto"
|
|
"github.com/nhost/nhost/services/auth/go/hibp"
|
|
"github.com/nhost/nhost/services/auth/go/middleware"
|
|
"github.com/nhost/nhost/services/auth/go/middleware/ratelimit"
|
|
"github.com/nhost/nhost/services/auth/go/oidc"
|
|
"github.com/nhost/nhost/services/auth/go/providers"
|
|
"github.com/nhost/nhost/services/auth/go/sql"
|
|
"github.com/urfave/cli/v3"
|
|
)
|
|
|
|
const (
|
|
flagAPIPrefix = "api-prefix"
|
|
flagPort = "port"
|
|
flagDebug = "debug"
|
|
flagLogFormatTEXT = "log-format-text"
|
|
flagEncryptionKey = "encryption-key"
|
|
flagTrustedProxies = "trusted-proxies"
|
|
flagPostgresConnection = "postgres"
|
|
flagPostgresMigrationsConnection = "postgres-migrations"
|
|
flagDisableSignup = "disable-signup"
|
|
flagConcealErrors = "conceal-errors"
|
|
flagDefaultAllowedRoles = "default-allowed-roles"
|
|
flagDefaultRole = "default-role"
|
|
flagDefaultLocale = "default-locale"
|
|
flagAllowedLocales = "allowed-locales"
|
|
flagDisableNewUsers = "disable-new-users"
|
|
flagGravatarEnabled = "gravatar-enabled"
|
|
flagGravatarDefault = "gravatar-default"
|
|
flagGravatarRating = "gravatar-rating"
|
|
flagRefreshTokenExpiresIn = "refresh-token-expires-in"
|
|
flagAccessTokensExpiresIn = "access-tokens-expires-in"
|
|
flagHasuraGraphqlJWTSecret = "hasura-graphql-jwt-secret" //nolint:gosec
|
|
flagEmailSigninEmailVerifiedRequired = "email-verification-required"
|
|
flagSMTPHost = "smtp-host"
|
|
flagSMTPPort = "smtp-port"
|
|
flagSMTPSecure = "smtp-secure"
|
|
flagSMTPUser = "smtp-user"
|
|
flagSMTPPassword = "smtp-password"
|
|
flagSMTPSender = "smtp-sender"
|
|
flagSMTPAPIHedaer = "smtp-api-header"
|
|
flagSMTPAuthMethod = "smtp-auth-method"
|
|
flagClientURL = "client-url"
|
|
flagServerURL = "server-url"
|
|
flagAllowRedirectURLs = "allow-redirect-urls"
|
|
flagEnableChangeEnv = "enable-change-env"
|
|
flagCustomClaims = "custom-claims"
|
|
flagCustomClaimsDefaults = "custom-claims-defaults"
|
|
flagGraphqlURL = "graphql-url"
|
|
flagHasuraAdminSecret = "hasura-admin-secret" //nolint:gosec
|
|
flagPasswordMinLength = "password-min-length"
|
|
flagPasswordHIBPEnabled = "password-hibp-enabled"
|
|
flagEmailTemplatesPath = "templates-path"
|
|
flagBlockedEmailDomains = "block-email-domains"
|
|
flagBlockedEmails = "block-emails"
|
|
flagAllowedEmailDomains = "allowed-email-domains"
|
|
flagAllowedEmails = "allowed-emails"
|
|
flagEmailPasswordlessEnabled = "email-passwordless-enabled"
|
|
flagRequireElevatedClaim = "require-elevated-claim"
|
|
flagWebauthnEnabled = "webauthn-enabled"
|
|
flagWebauhtnRPName = "webauthn-rp-name"
|
|
flagWebauthnRPID = "webauthn-rp-id"
|
|
flagWebauthnRPOrigins = "webauthn-rp-origins"
|
|
flagWebauthnAttestationTimeout = "webauthn-attestation-timeout"
|
|
flagRateLimitEnable = "rate-limit-enable"
|
|
flagRateLimitGlobalBurst = "rate-limit-global-burst"
|
|
flagRateLimitGlobalInterval = "rate-limit-global-interval"
|
|
flagRateLimitEmailBurst = "rate-limit-email-burst"
|
|
flagRateLimitEmailInterval = "rate-limit-email-interval"
|
|
flagRateLimitEmailIsGlobal = "rate-limit-email-is-global"
|
|
flagRateLimitSMSBurst = "rate-limit-sms-burst"
|
|
flagRateLimitSMSInterval = "rate-limit-sms-interval"
|
|
flagRateLimitBruteForceBurst = "rate-limit-brute-force-burst"
|
|
flagRateLimitBruteForceInterval = "rate-limit-brute-force-interval"
|
|
flagRateLimitSignupsBurst = "rate-limit-signups-burst"
|
|
flagRateLimitSignupsInterval = "rate-limit-signups-interval"
|
|
flagRateLimitMemcacheServer = "rate-limit-memcache-server"
|
|
flagRateLimitMemcachePrefix = "rate-limit-memcache-prefix"
|
|
flagTurnstileSecret = "turnstile-secret"
|
|
flagAppleAudience = "apple-audience"
|
|
flagGoogleAudience = "google-audience"
|
|
flagOTPEmailEnabled = "otp-email-enabled"
|
|
flagSMSPasswordlessEnabled = "sms-passwordless-enabled"
|
|
flagSMSProvider = "sms-provider"
|
|
flagSMSTwilioAccountSid = "sms-twilio-account-sid"
|
|
flagSMSTwilioAuthToken = "sms-twilio-auth-token" //nolint:gosec
|
|
flagSMSTwilioMessagingServiceID = "sms-twilio-messaging-service-id"
|
|
flagSMSModicaUsername = "sms-modica-username"
|
|
flagSMSModicaPassword = "sms-modica-password" //nolint:gosec
|
|
flagAnonymousUsersEnabled = "enable-anonymous-users"
|
|
flagMfaEnabled = "mfa-enabled"
|
|
flagMfaTotpIssuer = "mfa-totp-issuer"
|
|
flagGithubEnabled = "github-enabled"
|
|
flagGithubClientID = "github-client-id"
|
|
flagGithubClientSecret = "github-client-secret" //nolint:gosec
|
|
flagGithubAuthorizationURL = "github-authorization-url"
|
|
flagGithubTokenURL = "github-token-url" //nolint:gosec
|
|
flagGithubUserProfileURL = "github-user-profile-url"
|
|
flagGithubScope = "github-scope"
|
|
flagGoogleEnabled = "google-enabled"
|
|
flagGoogleClientID = "google-client-id"
|
|
flagGoogleClientSecret = "google-client-secret"
|
|
flagGoogleScope = "google-scope"
|
|
flagAppleEnabled = "apple-enabled"
|
|
flagAppleClientID = "apple-client-id"
|
|
flagAppleTeamID = "apple-team-id"
|
|
flagAppleKeyID = "apple-key-id"
|
|
flagApplePrivateKey = "apple-private-key"
|
|
flagAppleScope = "apple-scope"
|
|
flagLinkedInEnabled = "linkedin-enabled"
|
|
flagLinkedInClientID = "linkedin-client-id"
|
|
flagLinkedInClientSecret = "linkedin-client-secret"
|
|
flagLinkedInScope = "linkedin-scope"
|
|
flagDiscordEnabled = "discord-enabled"
|
|
flagDiscordClientID = "discord-client-id"
|
|
flagDiscordClientSecret = "discord-client-secret"
|
|
flagDiscordScope = "discord-scope"
|
|
flagSpotifyEnabled = "spotify-enabled"
|
|
flagSpotifyClientID = "spotify-client-id"
|
|
flagSpotifyClientSecret = "spotify-client-secret" //nolint:gosec
|
|
flagSpotifyScope = "spotify-scope"
|
|
flagTwitchEnabled = "twitch-enabled"
|
|
flagTwitchClientID = "twitch-client-id"
|
|
flagTwitchClientSecret = "twitch-client-secret"
|
|
flagTwitchScope = "twitch-scope"
|
|
flagGitlabEnabled = "gitlab-enabled"
|
|
flagGitlabClientID = "gitlab-client-id"
|
|
flagGitlabClientSecret = "gitlab-client-secret" //nolint:gosec
|
|
flagGitlabScope = "gitlab-scope"
|
|
flagBitbucketEnabled = "bitbucket-enabled"
|
|
flagBitbucketClientID = "bitbucket-client-id"
|
|
flagBitbucketClientSecret = "bitbucket-client-secret"
|
|
flagBitbucketScope = "bitbucket-scope"
|
|
flagWorkosEnabled = "workos-enabled"
|
|
flagWorkosClientID = "workos-client-id"
|
|
flagWorkosClientSecret = "workos-client-secret" //nolint:gosec
|
|
flagWorkosDefaultOrganization = "workos-default-organization"
|
|
flagWorkosDefaultConnection = "workos-default-connection"
|
|
flagWorkosDefaultDomain = "workos-default-domain"
|
|
flagWorkosScope = "workos-scope"
|
|
flagAzureadEnabled = "azuread-enabled"
|
|
flagAzureadClientID = "azuread-client-id"
|
|
flagAzureadClientSecret = "azuread-client-secret" //nolint:gosec
|
|
flagAzureadTenant = "azuread-tenant"
|
|
flagAzureadScope = "azuread-scope"
|
|
flagEntraIDEnabled = "entraid-enabled"
|
|
flagEntraIDClientID = "entraid-client-id"
|
|
flagEntraIDClientSecret = "entraid-client-secret" //nolint:gosec
|
|
flagEntraIDTenant = "entraid-tenant"
|
|
flagEntraIDScope = "entraid-scope"
|
|
flagFacebookEnabled = "facebook-enabled"
|
|
flagFacebookClientID = "facebook-client-id"
|
|
flagFacebookClientSecret = "facebook-client-secret"
|
|
flagFacebookScope = "facebook-scope"
|
|
flagWindowsliveEnabled = "windowslive-enabled"
|
|
flagWindowsliveClientID = "windowslive-client-id"
|
|
flagWindowsliveClientSecret = "windowslive-client-secret"
|
|
flagWindowsliveScope = "windowslive-scope"
|
|
flagStravaEnabled = "strava-enabled"
|
|
flagStravaClientID = "strava-client-id"
|
|
flagStravaClientSecret = "strava-client-secret" //nolint:gosec
|
|
flagStravaScope = "strava-scope"
|
|
flagTwitterEnabled = "twitter-enabled"
|
|
flagTwitterConsumerKey = "twitter-consumer-key"
|
|
flagTwitterConsumerSecret = "twitter-consumer-secret"
|
|
)
|
|
|
|
func CommandServe() *cli.Command { //nolint:funlen,maintidx
|
|
return &cli.Command{ //nolint: exhaustruct
|
|
Name: "serve",
|
|
Usage: "Serve the application",
|
|
//nolint:lll
|
|
Flags: []cli.Flag{
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagAPIPrefix,
|
|
Usage: "prefix for all routes",
|
|
Value: "",
|
|
Category: "server",
|
|
Sources: cli.EnvVars("AUTH_API_PREFIX"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagPort,
|
|
Usage: "Port to bind to",
|
|
Value: "4000",
|
|
Category: "server",
|
|
Sources: cli.EnvVars("AUTH_PORT"),
|
|
},
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagDebug,
|
|
Usage: "enable debug logging",
|
|
Category: "general",
|
|
Sources: cli.EnvVars("AUTH_DEBUG"),
|
|
},
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagLogFormatTEXT,
|
|
Usage: "format logs in plain text",
|
|
Category: "general",
|
|
Sources: cli.EnvVars("AUTH_LOG_FORMAT_TEXT"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagEncryptionKey,
|
|
Usage: "32 bytes encryption key used to encrypt sensitive data. Must be a hex-encoded string",
|
|
Category: "security",
|
|
Sources: cli.EnvVars("AUTH_ENCRYPTION_KEY"),
|
|
Required: true,
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagPostgresConnection,
|
|
Usage: "PostgreSQL connection URI: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING",
|
|
Value: "postgres://postgres:postgres@localhost:5432/local?sslmode=disable",
|
|
Category: "postgres",
|
|
Sources: cli.EnvVars("POSTGRES_CONNECTION", "HASURA_GRAPHQL_DATABASE_URL"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagPostgresMigrationsConnection,
|
|
Usage: "PostgreSQL connection URI for running migrations: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING. Required to inject the `auth` schema into the database. If not specied, the `postgres connection will be used",
|
|
Category: "postgres",
|
|
Sources: cli.EnvVars("POSTGRES_MIGRATIONS_CONNECTION"),
|
|
},
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagDisableSignup,
|
|
Usage: "If set to true, all signup methods will throw an unauthorized error",
|
|
Value: false,
|
|
Category: "signup",
|
|
Sources: cli.EnvVars("AUTH_DISABLE_SIGNUP"),
|
|
},
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagConcealErrors,
|
|
Usage: "Conceal sensitive error messages to avoid leaking information about user accounts to attackers",
|
|
Value: false,
|
|
Category: "server",
|
|
Sources: cli.EnvVars("AUTH_CONCEAL_ERRORS"),
|
|
},
|
|
&cli.StringSliceFlag{ //nolint: exhaustruct
|
|
Name: flagDefaultAllowedRoles,
|
|
Usage: "Comma-separated list of default allowed user roles",
|
|
Category: "signup",
|
|
Value: []string{"me"},
|
|
Sources: cli.EnvVars("AUTH_USER_DEFAULT_ALLOWED_ROLES"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagDefaultRole,
|
|
Usage: "Default user role for registered users",
|
|
Category: "signup",
|
|
Value: "user",
|
|
Sources: cli.EnvVars("AUTH_USER_DEFAULT_ROLE"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagDefaultLocale,
|
|
Usage: "Default locale",
|
|
Category: "signup",
|
|
Value: "en",
|
|
Sources: cli.EnvVars("AUTH_LOCALE_DEFAULT"),
|
|
},
|
|
&cli.StringSliceFlag{ //nolint: exhaustruct
|
|
Name: flagAllowedLocales,
|
|
Usage: "Allowed locales",
|
|
Category: "signup",
|
|
Value: []string{"en"},
|
|
Sources: cli.EnvVars("AUTH_LOCALE_ALLOWED_LOCALES"),
|
|
},
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagDisableNewUsers,
|
|
Usage: "If set, new users will be disabled after finishing registration and won't be able to sign in",
|
|
Category: "signup",
|
|
Sources: cli.EnvVars("AUTH_DISABLE_NEW_USERS"),
|
|
},
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagGravatarEnabled,
|
|
Usage: "Enable gravatar",
|
|
Category: "signup",
|
|
Value: true,
|
|
Sources: cli.EnvVars("AUTH_GRAVATAR_ENABLED"),
|
|
},
|
|
&cli.GenericFlag{ //nolint: exhaustruct
|
|
Name: flagGravatarDefault,
|
|
Value: &EnumValue{ //nolint: exhaustruct
|
|
Enum: []string{
|
|
"blank",
|
|
"identicon",
|
|
"monsterid",
|
|
"wavatar",
|
|
"retro",
|
|
"robohash",
|
|
"mp",
|
|
"404",
|
|
},
|
|
Default: "blank",
|
|
},
|
|
Usage: "Gravatar default",
|
|
Category: "signup",
|
|
Sources: cli.EnvVars("AUTH_GRAVATAR_DEFAULT"),
|
|
},
|
|
&cli.GenericFlag{ //nolint: exhaustruct
|
|
Name: flagGravatarRating,
|
|
Value: &EnumValue{ //nolint: exhaustruct
|
|
Enum: []string{
|
|
"g",
|
|
"pg",
|
|
"r",
|
|
"x",
|
|
},
|
|
Default: "g",
|
|
},
|
|
Usage: "Gravatar rating",
|
|
Category: "signup",
|
|
Sources: cli.EnvVars("AUTH_GRAVATAR_RATING"),
|
|
},
|
|
&cli.IntFlag{ //nolint: exhaustruct
|
|
Name: flagRefreshTokenExpiresIn,
|
|
Usage: "Refresh token expires in (seconds)",
|
|
Value: 2592000, //nolint:mnd
|
|
Category: "jwt",
|
|
Sources: cli.EnvVars("AUTH_REFRESH_TOKEN_EXPIRES_IN"),
|
|
},
|
|
&cli.IntFlag{ //nolint: exhaustruct
|
|
Name: flagAccessTokensExpiresIn,
|
|
Usage: "Access tokens expires in (seconds)",
|
|
Value: 900, //nolint:mnd
|
|
Category: "jwt",
|
|
Sources: cli.EnvVars("AUTH_ACCESS_TOKEN_EXPIRES_IN"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagHasuraGraphqlJWTSecret,
|
|
Usage: "Key used for generating JWTs. Must be `HMAC-SHA`-based and the same as configured in Hasura. More info: https://hasura.io/docs/latest/graphql/core/auth/authentication/jwt.html#running-with-jwt",
|
|
Category: "jwt",
|
|
Sources: cli.EnvVars("HASURA_GRAPHQL_JWT_SECRET"),
|
|
},
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagEmailSigninEmailVerifiedRequired,
|
|
Usage: "Require email to be verified for email signin",
|
|
Category: "signup",
|
|
Value: true,
|
|
Sources: cli.EnvVars("AUTH_EMAIL_SIGNIN_EMAIL_VERIFIED_REQUIRED"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagSMTPHost,
|
|
Usage: "SMTP Host. If the host is 'postmark' then the Postmark API will be used. Use AUTH_SMTP_PASS as the server token, other SMTP options are ignored",
|
|
Category: "smtp",
|
|
Sources: cli.EnvVars("AUTH_SMTP_HOST"),
|
|
},
|
|
&cli.UintFlag{ //nolint: exhaustruct
|
|
Name: flagSMTPPort,
|
|
Usage: "SMTP port",
|
|
Category: "smtp",
|
|
Value: 587, //nolint:mnd
|
|
Sources: cli.EnvVars("AUTH_SMTP_PORT"),
|
|
},
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagSMTPSecure,
|
|
Usage: "Connect over TLS. Deprecated: It is recommended to use port 587 with STARTTLS instead of this option.",
|
|
Category: "smtp",
|
|
Sources: cli.EnvVars("AUTH_SMTP_SECURE"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagSMTPUser,
|
|
Usage: "SMTP user",
|
|
Category: "smtp",
|
|
Sources: cli.EnvVars("AUTH_SMTP_USER"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagSMTPPassword,
|
|
Usage: "SMTP password",
|
|
Category: "smtp",
|
|
Sources: cli.EnvVars("AUTH_SMTP_PASS"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagSMTPSender,
|
|
Usage: "SMTP sender",
|
|
Category: "smtp",
|
|
Sources: cli.EnvVars("AUTH_SMTP_SENDER"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagSMTPAPIHedaer,
|
|
Usage: "SMTP API Header. Maps to header X-SMTPAPI",
|
|
Category: "smtp",
|
|
Sources: cli.EnvVars("AUTH_SMTP_X_SMTPAPI_HEADER"),
|
|
},
|
|
&cli.GenericFlag{ //nolint: exhaustruct
|
|
Name: flagSMTPAuthMethod,
|
|
Value: &EnumValue{ //nolint: exhaustruct
|
|
Enum: []string{
|
|
"LOGIN",
|
|
"PLAIN",
|
|
"CRAM-MD5",
|
|
},
|
|
Default: "PLAIN",
|
|
},
|
|
Usage: "SMTP Authentication method",
|
|
Category: "smtp",
|
|
Sources: cli.EnvVars("AUTH_SMTP_AUTH_METHOD"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagClientURL,
|
|
Usage: "URL of your frontend application. Used to redirect users to the right page once actions based on emails or OAuth succeed",
|
|
Category: "application",
|
|
Sources: cli.EnvVars("AUTH_CLIENT_URL"),
|
|
},
|
|
&cli.StringSliceFlag{ //nolint:exhaustruct
|
|
Name: flagAllowRedirectURLs,
|
|
Usage: "Allowed redirect URLs",
|
|
Category: "application",
|
|
Sources: cli.EnvVars("AUTH_ACCESS_CONTROL_ALLOWED_REDIRECT_URLS"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagServerURL,
|
|
Usage: "Server URL of where Auth service is running. This value is to used as a callback in email templates and for the OAuth authentication process",
|
|
Category: "server",
|
|
Sources: cli.EnvVars("AUTH_SERVER_URL"),
|
|
},
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagEnableChangeEnv,
|
|
Usage: "Enable change env. Do not do this in production!",
|
|
Category: "server",
|
|
Sources: cli.EnvVars("AUTH_ENABLE_CHANGE_ENV"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagCustomClaims,
|
|
Usage: "Custom claims",
|
|
Category: "jwt",
|
|
Sources: cli.EnvVars("AUTH_JWT_CUSTOM_CLAIMS"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagCustomClaimsDefaults,
|
|
Usage: "Custom claims defaults",
|
|
Category: "jwt",
|
|
Sources: cli.EnvVars("AUTH_JWT_CUSTOM_CLAIMS_DEFAULTS"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagGraphqlURL,
|
|
Usage: "Hasura GraphQL endpoint. Required for custom claims",
|
|
Category: "jwt",
|
|
Sources: cli.EnvVars("HASURA_GRAPHQL_GRAPHQL_URL"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagHasuraAdminSecret,
|
|
Usage: "Hasura admin secret. Required for custom claims",
|
|
Category: "jwt",
|
|
Sources: cli.EnvVars("HASURA_GRAPHQL_ADMIN_SECRET"),
|
|
},
|
|
&cli.IntFlag{ //nolint: exhaustruct
|
|
Name: flagPasswordMinLength,
|
|
Usage: "Minimum password length",
|
|
Value: 3, //nolint:mnd
|
|
Category: "signup",
|
|
Sources: cli.EnvVars("AUTH_PASSWORD_MIN_LENGTH"),
|
|
},
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagPasswordHIBPEnabled,
|
|
Usage: "Check user's password against Pwned Passwords https://haveibeenpwned.com/Passwords",
|
|
Category: "signup",
|
|
Sources: cli.EnvVars("AUTH_PASSWORD_HIBP_ENABLED"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagEmailTemplatesPath,
|
|
Usage: "Path to the email templates. Default to included ones if path isn't found",
|
|
Value: "/app/email-templates",
|
|
Category: "email",
|
|
Sources: cli.EnvVars("AUTH_EMAIL_TEMPLATES_PATH"),
|
|
},
|
|
&cli.StringSliceFlag{ //nolint: exhaustruct
|
|
Name: flagBlockedEmailDomains,
|
|
Usage: "Comma-separated list of email domains that cannot register",
|
|
Category: "signup",
|
|
Sources: cli.EnvVars("AUTH_ACCESS_CONTROL_BLOCKED_EMAIL_DOMAINS"),
|
|
},
|
|
&cli.StringSliceFlag{ //nolint: exhaustruct
|
|
Name: flagBlockedEmails,
|
|
Usage: "Comma-separated list of email domains that cannot register",
|
|
Category: "signup",
|
|
Sources: cli.EnvVars("AUTH_ACCESS_CONTROL_BLOCKED_EMAILS"),
|
|
},
|
|
&cli.StringSliceFlag{ //nolint: exhaustruct
|
|
Name: flagAllowedEmailDomains,
|
|
Usage: "Comma-separated list of email domains that can register",
|
|
Category: "signup",
|
|
Sources: cli.EnvVars("AUTH_ACCESS_CONTROL_ALLOWED_EMAIL_DOMAINS"),
|
|
},
|
|
&cli.StringSliceFlag{ //nolint: exhaustruct
|
|
Name: flagAllowedEmails,
|
|
Usage: "Comma-separated list of emails that can register",
|
|
Category: "signup",
|
|
Sources: cli.EnvVars("AUTH_ACCESS_CONTROL_ALLOWED_EMAILS"),
|
|
},
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagEmailPasswordlessEnabled,
|
|
Usage: "Enables passwordless authentication by email. SMTP must be configured",
|
|
Value: false,
|
|
Category: "signin",
|
|
Sources: cli.EnvVars("AUTH_EMAIL_PASSWORDLESS_ENABLED"),
|
|
},
|
|
&cli.GenericFlag{ //nolint: exhaustruct
|
|
Name: flagRequireElevatedClaim,
|
|
Value: &EnumValue{ //nolint: exhaustruct
|
|
Enum: []string{
|
|
"disabled",
|
|
"recommended",
|
|
"required",
|
|
},
|
|
Default: "disabled",
|
|
},
|
|
Usage: "Require x-hasura-auth-elevated claim to perform certain actions: create PATs, change email and/or password, enable/disable MFA and add security keys. If set to `recommended` the claim check is only performed if the user has a security key attached. If set to `required` the only action that won't require the claim is setting a security key for the first time.",
|
|
Category: "security",
|
|
Sources: cli.EnvVars("AUTH_REQUIRE_ELEVATED_CLAIM"),
|
|
},
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagWebauthnEnabled,
|
|
Usage: "When enabled, passwordless Webauthn authentication can be done via device supported strong authenticators like fingerprint, Face ID, etc.",
|
|
Value: false,
|
|
Category: "webauthn",
|
|
Sources: cli.EnvVars("AUTH_WEBAUTHN_ENABLED"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagWebauhtnRPName,
|
|
Usage: "Relying party name. Friendly name visual to the user informing who requires the authentication. Probably your app's name",
|
|
Category: "webauthn",
|
|
Sources: cli.EnvVars("AUTH_WEBAUTHN_RP_NAME"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagWebauthnRPID,
|
|
Usage: "Relying party id. If not set `AUTH_CLIENT_URL` will be used as a default",
|
|
Category: "webauthn",
|
|
Sources: cli.EnvVars("AUTH_WEBAUTHN_RP_ID"),
|
|
},
|
|
&cli.StringSliceFlag{ //nolint: exhaustruct
|
|
Name: flagWebauthnRPOrigins,
|
|
Usage: "Array of URLs where the registration is permitted and should have occurred on. `AUTH_CLIENT_URL` will be automatically added to the list of origins if is set",
|
|
Category: "webauthn",
|
|
Sources: cli.EnvVars("AUTH_WEBAUTHN_RP_ORIGINS"),
|
|
},
|
|
&cli.IntFlag{ //nolint: exhaustruct
|
|
Name: flagWebauthnAttestationTimeout,
|
|
Usage: "Timeout for the attestation process in milliseconds",
|
|
Value: 60000, //nolint:mnd
|
|
Category: "webauthn",
|
|
Sources: cli.EnvVars("AUTH_WEBAUTHN_ATTESTATION_TIMEOUT"),
|
|
},
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagRateLimitEnable,
|
|
Usage: "Enable rate limiting",
|
|
Value: false,
|
|
Category: "rate-limit",
|
|
Sources: cli.EnvVars("AUTH_RATE_LIMIT_ENABLE"),
|
|
},
|
|
&cli.IntFlag{ //nolint: exhaustruct
|
|
Name: flagRateLimitGlobalBurst,
|
|
Usage: "Global rate limit burst",
|
|
Value: 100, //nolint:mnd
|
|
Category: "rate-limit",
|
|
Sources: cli.EnvVars("AUTH_RATE_LIMIT_GLOBAL_BURST"),
|
|
},
|
|
&cli.DurationFlag{ //nolint: exhaustruct
|
|
Name: flagRateLimitGlobalInterval,
|
|
Usage: "Global rate limit interval",
|
|
Value: time.Minute,
|
|
Category: "rate-limit",
|
|
Sources: cli.EnvVars("AUTH_RATE_LIMIT_GLOBAL_INTERVAL"),
|
|
},
|
|
&cli.IntFlag{ //nolint: exhaustruct
|
|
Name: flagRateLimitEmailBurst,
|
|
Usage: "Email rate limit burst",
|
|
Value: 10, //nolint:mnd
|
|
Category: "rate-limit",
|
|
Sources: cli.EnvVars("AUTH_RATE_LIMIT_EMAIL_BURST"),
|
|
},
|
|
&cli.DurationFlag{ //nolint: exhaustruct
|
|
Name: flagRateLimitEmailInterval,
|
|
Usage: "Email rate limit interval",
|
|
Value: time.Hour,
|
|
Category: "rate-limit",
|
|
Sources: cli.EnvVars("AUTH_RATE_LIMIT_EMAIL_INTERVAL"),
|
|
},
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagRateLimitEmailIsGlobal,
|
|
Usage: "Email rate limit is global instead of per user",
|
|
Value: false,
|
|
Category: "rate-limit",
|
|
Sources: cli.EnvVars("AUTH_RATE_LIMIT_EMAIL_IS_GLOBAL"),
|
|
},
|
|
&cli.IntFlag{ //nolint: exhaustruct
|
|
Name: flagRateLimitSMSBurst,
|
|
Usage: "SMS rate limit burst",
|
|
Value: 10, //nolint:mnd
|
|
Category: "rate-limit",
|
|
Sources: cli.EnvVars("AUTH_RATE_LIMIT_SMS_BURST"),
|
|
},
|
|
&cli.DurationFlag{ //nolint: exhaustruct
|
|
Name: flagRateLimitSMSInterval,
|
|
Usage: "SMS rate limit interval",
|
|
Value: time.Hour,
|
|
Category: "rate-limit",
|
|
Sources: cli.EnvVars("AUTH_RATE_LIMIT_SMS_INTERVAL"),
|
|
},
|
|
&cli.IntFlag{ //nolint: exhaustruct
|
|
Name: flagRateLimitBruteForceBurst,
|
|
Usage: "Brute force rate limit burst",
|
|
Value: 10, //nolint:mnd
|
|
Category: "rate-limit",
|
|
Sources: cli.EnvVars("AUTH_RATE_LIMIT_BRUTE_FORCE_BURST"),
|
|
},
|
|
&cli.DurationFlag{ //nolint: exhaustruct
|
|
Name: flagRateLimitBruteForceInterval,
|
|
Usage: "Brute force rate limit interval",
|
|
Value: 5 * time.Minute, //nolint:mnd
|
|
Category: "rate-limit",
|
|
Sources: cli.EnvVars("AUTH_RATE_LIMIT_BRUTE_FORCE_INTERVAL"),
|
|
},
|
|
&cli.IntFlag{ //nolint: exhaustruct
|
|
Name: flagRateLimitSignupsBurst,
|
|
Usage: "Signups rate limit burst",
|
|
Value: 10, //nolint:mnd
|
|
Category: "rate-limit",
|
|
Sources: cli.EnvVars("AUTH_RATE_LIMIT_SIGNUPS_BURST"),
|
|
},
|
|
&cli.DurationFlag{ //nolint: exhaustruct
|
|
Name: flagRateLimitSignupsInterval,
|
|
Usage: "Signups rate limit interval",
|
|
Value: 5 * time.Minute, //nolint:mnd
|
|
Category: "rate-limit",
|
|
Sources: cli.EnvVars("AUTH_RATE_LIMIT_SIGNUPS_INTERVAL"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagRateLimitMemcacheServer,
|
|
Usage: "Store sliding window rate limit data in memcache",
|
|
Category: "rate-limit",
|
|
Sources: cli.EnvVars("AUTH_RATE_LIMIT_MEMCACHE_SERVER"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagRateLimitMemcachePrefix,
|
|
Usage: "Prefix for rate limit keys in memcache",
|
|
Category: "rate-limit",
|
|
Sources: cli.EnvVars("AUTH_RATE_LIMIT_MEMCACHE_PREFIX"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagTurnstileSecret,
|
|
Usage: "Turnstile secret. If passed, enable Cloudflare's turnstile for signup methods. The header `X-Cf-Turnstile-Response ` will have to be included in the request for verification",
|
|
Category: "turnstile",
|
|
Sources: cli.EnvVars("AUTH_TURNSTILE_SECRET"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagAppleAudience,
|
|
Usage: "Apple Audience. Used to verify the audience on JWT tokens provided by Apple. Needed for idtoken validation",
|
|
Category: "apple",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_APPLE_AUDIENCE"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagGoogleAudience,
|
|
Usage: "Google Audience. Used to verify the audience on JWT tokens provided by Google. Needed for idtoken validation",
|
|
Category: "google",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_GOOGLE_AUDIENCE"),
|
|
},
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagOTPEmailEnabled,
|
|
Usage: "Enable OTP via email",
|
|
Category: "otp",
|
|
Sources: cli.EnvVars("AUTH_OTP_EMAIL_ENABLED"),
|
|
},
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagSMSPasswordlessEnabled,
|
|
Usage: "Enable SMS passwordless authentication",
|
|
Category: "sms",
|
|
Sources: cli.EnvVars("AUTH_SMS_PASSWORDLESS_ENABLED"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagSMSProvider,
|
|
Usage: "SMS provider (twilio or modica)",
|
|
Category: "sms",
|
|
Value: "twilio",
|
|
Sources: cli.EnvVars("AUTH_SMS_PROVIDER"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagSMSTwilioAccountSid,
|
|
Usage: "Twilio Account SID for SMS",
|
|
Category: "sms",
|
|
Sources: cli.EnvVars("AUTH_SMS_TWILIO_ACCOUNT_SID"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagSMSTwilioAuthToken,
|
|
Usage: "Twilio Auth Token for SMS",
|
|
Category: "sms",
|
|
Sources: cli.EnvVars("AUTH_SMS_TWILIO_AUTH_TOKEN"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagSMSTwilioMessagingServiceID,
|
|
Usage: "Twilio Messaging Service ID for SMS",
|
|
Category: "sms",
|
|
Sources: cli.EnvVars("AUTH_SMS_TWILIO_MESSAGING_SERVICE_ID"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagSMSModicaUsername,
|
|
Usage: "Modica username for SMS",
|
|
Category: "sms",
|
|
Sources: cli.EnvVars("AUTH_SMS_MODICA_USERNAME"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagSMSModicaPassword,
|
|
Usage: "Modica password for SMS",
|
|
Category: "sms",
|
|
Sources: cli.EnvVars("AUTH_SMS_MODICA_PASSWORD"),
|
|
},
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagAnonymousUsersEnabled,
|
|
Usage: "Enable anonymous users",
|
|
Category: "signup",
|
|
Value: false,
|
|
Sources: cli.EnvVars("AUTH_ANONYMOUS_USERS_ENABLED"),
|
|
},
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagMfaEnabled,
|
|
Usage: "Enable MFA",
|
|
Category: "mfa",
|
|
Value: false,
|
|
Sources: cli.EnvVars("AUTH_MFA_ENABLED"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagMfaTotpIssuer,
|
|
Usage: "Issuer for MFA TOTP",
|
|
Category: "mfa",
|
|
Value: "auth",
|
|
Sources: cli.EnvVars("AUTH_MFA_TOTP_ISSUER"),
|
|
},
|
|
// GitHub provider flags
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagGithubEnabled,
|
|
Usage: "Enable GitHub OAuth provider",
|
|
Category: "oauth-github",
|
|
Value: false,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_GITHUB_ENABLED"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagGithubClientID,
|
|
Usage: "GitHub OAuth client ID",
|
|
Category: "oauth-github",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_GITHUB_CLIENT_ID"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagGithubClientSecret,
|
|
Usage: "GitHub OAuth client secret",
|
|
Category: "oauth-github",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_GITHUB_CLIENT_SECRET"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagGithubAuthorizationURL,
|
|
Usage: "GitHub OAuth authorization URL",
|
|
Category: "oauth-github",
|
|
Value: "https://github.com/login/oauth/authorize",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_GITHUB_AUTHORIZATION_URL"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagGithubTokenURL,
|
|
Usage: "GitHub OAuth token URL",
|
|
Category: "oauth-github",
|
|
Value: "https://github.com/login/oauth/access_token",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_GITHUB_TOKEN_URL"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagGithubUserProfileURL,
|
|
Usage: "GitHub OAuth user profile URL",
|
|
Category: "oauth-github",
|
|
Value: "https://api.github.com/user",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_GITHUB_USER_PROFILE_URL"),
|
|
},
|
|
&cli.StringSliceFlag{ //nolint: exhaustruct
|
|
Name: flagGithubScope,
|
|
Usage: "GitHub OAuth scope",
|
|
Category: "oauth-github",
|
|
Value: providers.DefaultGithubScopes,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_GITHUB_SCOPE"),
|
|
},
|
|
// Google provider flags
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagGoogleEnabled,
|
|
Usage: "Enable Google OAuth provider",
|
|
Category: "oauth-google",
|
|
Value: false,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_GOOGLE_ENABLED"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagGoogleClientID,
|
|
Usage: "Google OAuth client ID",
|
|
Category: "oauth-google",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_GOOGLE_CLIENT_ID"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagGoogleClientSecret,
|
|
Usage: "Google OAuth client secret",
|
|
Category: "oauth-google",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_GOOGLE_CLIENT_SECRET"),
|
|
},
|
|
&cli.StringSliceFlag{ //nolint: exhaustruct
|
|
Name: flagGoogleScope,
|
|
Usage: "Google OAuth scope",
|
|
Category: "oauth-google",
|
|
Value: providers.DefaultGoogleScopes,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_GOOGLE_SCOPE"),
|
|
},
|
|
// Apple provider flags
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagAppleEnabled,
|
|
Usage: "Enable Apple OAuth provider",
|
|
Category: "oauth-apple",
|
|
Value: false,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_APPLE_ENABLED"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagAppleClientID,
|
|
Usage: "Apple OAuth client ID",
|
|
Category: "oauth-apple",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_APPLE_CLIENT_ID"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagAppleTeamID,
|
|
Usage: "Apple OAuth team ID",
|
|
Category: "oauth-apple",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_APPLE_TEAM_ID"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagAppleKeyID,
|
|
Usage: "Apple OAuth key ID",
|
|
Category: "oauth-apple",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_APPLE_KEY_ID"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagApplePrivateKey,
|
|
Usage: "Apple OAuth private key",
|
|
Category: "oauth-apple",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_APPLE_PRIVATE_KEY"),
|
|
},
|
|
&cli.StringSliceFlag{ //nolint: exhaustruct
|
|
Name: flagAppleScope,
|
|
Usage: "Apple OAuth scope",
|
|
Category: "oauth-apple",
|
|
Value: providers.DefaultAppleScopes,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_APPLE_SCOPE"),
|
|
},
|
|
// LinkedIn provider flags
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagLinkedInEnabled,
|
|
Usage: "Enable LinkedIn OAuth provider",
|
|
Category: "oauth-linkedin",
|
|
Value: false,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_LINKEDIN_ENABLED"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagLinkedInClientID,
|
|
Usage: "LinkedIn OAuth client ID",
|
|
Category: "oauth-linkedin",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_LINKEDIN_CLIENT_ID"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagLinkedInClientSecret,
|
|
Usage: "LinkedIn OAuth client secret",
|
|
Category: "oauth-linkedin",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_LINKEDIN_CLIENT_SECRET"),
|
|
},
|
|
&cli.StringSliceFlag{ //nolint: exhaustruct
|
|
Name: flagLinkedInScope,
|
|
Usage: "LinkedIn OAuth scope",
|
|
Category: "oauth-linkedin",
|
|
Value: providers.DefaultLinkedInScopes,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_LINKEDIN_SCOPE"),
|
|
},
|
|
// Discord provider flags
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagDiscordEnabled,
|
|
Usage: "Enable Discord OAuth provider",
|
|
Category: "oauth-discord",
|
|
Value: false,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_DISCORD_ENABLED"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagDiscordClientID,
|
|
Usage: "Discord OAuth client ID",
|
|
Category: "oauth-discord",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_DISCORD_CLIENT_ID"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagDiscordClientSecret,
|
|
Usage: "Discord OAuth client secret",
|
|
Category: "oauth-discord",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_DISCORD_CLIENT_SECRET"),
|
|
},
|
|
&cli.StringSliceFlag{ //nolint: exhaustruct
|
|
Name: flagDiscordScope,
|
|
Usage: "Discord OAuth scope",
|
|
Category: "oauth-discord",
|
|
Value: providers.DefaultDiscordScopes,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_DISCORD_SCOPE"),
|
|
},
|
|
// Spotify provider flags
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagSpotifyEnabled,
|
|
Usage: "Enable Spotify OAuth provider",
|
|
Category: "oauth-spotify",
|
|
Value: false,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_SPOTIFY_ENABLED"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagSpotifyClientID,
|
|
Usage: "Spotify OAuth client ID",
|
|
Category: "oauth-spotify",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_SPOTIFY_CLIENT_ID"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagSpotifyClientSecret,
|
|
Usage: "Spotify OAuth client secret",
|
|
Category: "oauth-spotify",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_SPOTIFY_CLIENT_SECRET"),
|
|
},
|
|
&cli.StringSliceFlag{ //nolint: exhaustruct
|
|
Name: flagSpotifyScope,
|
|
Usage: "Spotify OAuth scope",
|
|
Category: "oauth-spotify",
|
|
Value: providers.DefaultSpotifyScopes,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_SPOTIFY_SCOPE"),
|
|
},
|
|
|
|
// Twitch provider flags
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagTwitchEnabled,
|
|
Usage: "Enable Twitch OAuth provider",
|
|
Category: "oauth-twitch",
|
|
Value: false,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_TWITCH_ENABLED"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagTwitchClientID,
|
|
Usage: "Twitch OAuth client ID",
|
|
Category: "oauth-twitch",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_TWITCH_CLIENT_ID"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagTwitchClientSecret,
|
|
Usage: "Twitch OAuth client secret",
|
|
Category: "oauth-twitch",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_TWITCH_CLIENT_SECRET"),
|
|
},
|
|
&cli.StringSliceFlag{ //nolint: exhaustruct
|
|
Name: flagTwitchScope,
|
|
Usage: "Twitch OAuth scope",
|
|
Category: "oauth-twitch",
|
|
Value: providers.DefaultTwitchScopes,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_TWITCH_SCOPE"),
|
|
},
|
|
|
|
// Gitlab provider flags
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagGitlabEnabled,
|
|
Usage: "Enable Gitlab OAuth provider",
|
|
Category: "oauth-gitlab",
|
|
Value: false,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_GITLAB_ENABLED"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagGitlabClientID,
|
|
Usage: "Gitlab OAuth client ID",
|
|
Category: "oauth-gitlab",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_GITLAB_CLIENT_ID"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagGitlabClientSecret,
|
|
Usage: "Gitlab OAuth client secret",
|
|
Category: "oauth-gitlab",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_GITLAB_CLIENT_SECRET"),
|
|
},
|
|
&cli.StringSliceFlag{ //nolint: exhaustruct
|
|
Name: flagGitlabScope,
|
|
Usage: "Gitlab OAuth scope",
|
|
Category: "oauth-gitlab",
|
|
Value: providers.DefaultGitlabScopes,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_GITLAB_SCOPE"),
|
|
},
|
|
|
|
// Bitbucket provider flags
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagBitbucketEnabled,
|
|
Usage: "Enable Bitbucket OAuth provider",
|
|
Category: "oauth-bitbucket",
|
|
Value: false,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_BITBUCKET_ENABLED"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagBitbucketClientID,
|
|
Usage: "Bitbucket OAuth client ID",
|
|
Category: "oauth-bitbucket",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_BITBUCKET_CLIENT_ID"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagBitbucketClientSecret,
|
|
Usage: "Bitbucket OAuth client secret",
|
|
Category: "oauth-bitbucket",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_BITBUCKET_CLIENT_SECRET"),
|
|
},
|
|
&cli.StringSliceFlag{ //nolint: exhaustruct
|
|
Name: flagBitbucketScope,
|
|
Usage: "Bitbucket OAuth scope",
|
|
Category: "oauth-bitbucket",
|
|
Value: providers.DefaultBitbucketScopes,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_BITBUCKET_SCOPE"),
|
|
},
|
|
|
|
// WorkOS provider flags
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagWorkosEnabled,
|
|
Usage: "Enable WorkOS OAuth provider",
|
|
Category: "oauth-workos",
|
|
Value: false,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_WORKOS_ENABLED"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagWorkosClientID,
|
|
Usage: "WorkOS OAuth client ID",
|
|
Category: "oauth-workos",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_WORKOS_CLIENT_ID"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagWorkosClientSecret,
|
|
Usage: "WorkOS OAuth client secret",
|
|
Category: "oauth-workos",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_WORKOS_CLIENT_SECRET"),
|
|
},
|
|
&cli.StringFlag{ //nolint:exhaustruct
|
|
Name: flagWorkosDefaultOrganization,
|
|
Usage: "WorkOS OAuth default organization",
|
|
Category: "oauth-workos",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_WORKOS_DEFAULT_ORGANIZATION"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagWorkosDefaultConnection,
|
|
Usage: "WorkOS OAuth default connection",
|
|
Category: "oauth-workos",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_WORKOS_DEFAULT_CONNECTION"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagWorkosDefaultDomain,
|
|
Usage: "WorkOS OAuth default domain",
|
|
Category: "oauth-workos",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_WORKOS_DEFAULT_DOMAIN"),
|
|
},
|
|
// AzureAD provider flags
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagAzureadEnabled,
|
|
Usage: "Enable Azuread OAuth provider",
|
|
Category: "oauth-azuread",
|
|
Value: false,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_AZUREAD_ENABLED"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagAzureadClientID,
|
|
Usage: "AzureAD OAuth client ID",
|
|
Category: "oauth-azuread",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_AZUREAD_CLIENT_ID"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagAzureadClientSecret,
|
|
Usage: "Azuread OAuth client secret",
|
|
Category: "oauth-azuread",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_AZUREAD_CLIENT_SECRET"),
|
|
},
|
|
&cli.StringFlag{ //nolint:exhaustruct
|
|
Name: flagAzureadTenant,
|
|
Usage: "Azuread Tenant",
|
|
Category: "oauth-azuread",
|
|
Value: "common",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_AZUREAD_TENANT"),
|
|
},
|
|
&cli.StringSliceFlag{ //nolint: exhaustruct
|
|
Name: flagAzureadScope,
|
|
Usage: "Azuread OAuth scope",
|
|
Category: "oauth-azuread",
|
|
Value: providers.DefaultAzureadScopes,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_AZUREAD_SCOPE"),
|
|
},
|
|
// Microsoft EntraID flags
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagEntraIDEnabled,
|
|
Usage: "Enable EntraID OAuth provider",
|
|
Category: "oauth-entraid",
|
|
Value: false,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_ENTRAID_ENABLED"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagEntraIDClientID,
|
|
Usage: "EntraID OAuth client ID",
|
|
Category: "oauth-entraid",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_ENTRAID_CLIENT_ID"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagEntraIDClientSecret,
|
|
Usage: "EntraID OAuth client secret",
|
|
Category: "oauth-entraid",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_ENTRAID_CLIENT_SECRET"),
|
|
},
|
|
&cli.StringFlag{ //nolint:exhaustruct
|
|
Name: flagEntraIDTenant,
|
|
Usage: "EntraID Tenant",
|
|
Category: "oauth-entraid",
|
|
Value: "common",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_ENTRAID_TENANT"),
|
|
},
|
|
&cli.StringSliceFlag{ //nolint: exhaustruct
|
|
Name: flagEntraIDScope,
|
|
Usage: "EntraID OAuth scope",
|
|
Category: "oauth-entraid",
|
|
Value: providers.DefaultEntraIDScopes,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_ENTRAID_SCOPE"),
|
|
},
|
|
// Facebook provider flags
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagFacebookEnabled,
|
|
Usage: "Enable Facebook OAuth provider",
|
|
Category: "oauth-facebook",
|
|
Value: false,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_FACEBOOK_ENABLED"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagFacebookClientID,
|
|
Usage: "Facebook OAuth client ID",
|
|
Category: "oauth-facebook",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_FACEBOOK_CLIENT_ID"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagFacebookClientSecret,
|
|
Usage: "Facebook OAuth client secret",
|
|
Category: "oauth-facebook",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_FACEBOOK_CLIENT_SECRET"),
|
|
},
|
|
&cli.StringSliceFlag{ //nolint: exhaustruct
|
|
Name: flagFacebookScope,
|
|
Usage: "Facebook OAuth scope",
|
|
Category: "oauth-facebook",
|
|
Value: providers.DefaultFacebookScopes,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_FACEBOOK_SCOPE"),
|
|
},
|
|
// Windowslive provider flags
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagWindowsliveEnabled,
|
|
Usage: "Enable Windowslive OAuth provider",
|
|
Category: "oauth-windowslive",
|
|
Value: false,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_WINDOWS_LIVE_ENABLED"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagWindowsliveClientID,
|
|
Usage: "Windowslive OAuth client ID",
|
|
Category: "oauth-windowslive",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_WINDOWS_LIVE_CLIENT_ID"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagWindowsliveClientSecret,
|
|
Usage: "Windows Live OAuth client secret",
|
|
Category: "oauth-windowslive",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_WINDOWS_LIVE_CLIENT_SECRET"),
|
|
},
|
|
&cli.StringSliceFlag{ //nolint: exhaustruct
|
|
Name: flagWindowsliveScope,
|
|
Usage: "Windows Live OAuth scope",
|
|
Category: "oauth-windowslive",
|
|
Value: providers.DefaultWindowsliveScopes,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_WINDOWS_LIVE_SCOPE"),
|
|
},
|
|
|
|
// Strava provider flags
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagStravaEnabled,
|
|
Usage: "Enable Strava OAuth provider",
|
|
Category: "oauth-strava",
|
|
Value: false,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_STRAVA_ENABLED"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagStravaClientID,
|
|
Usage: "Strava OAuth client ID",
|
|
Category: "oauth-strava",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_STRAVA_CLIENT_ID"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagStravaClientSecret,
|
|
Usage: "Strava OAuth client secret",
|
|
Category: "oauth-strava",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_STRAVA_CLIENT_SECRET"),
|
|
},
|
|
&cli.StringSliceFlag{ //nolint: exhaustruct
|
|
Name: flagStravaScope,
|
|
Usage: "Strava OAuth scope",
|
|
Category: "oauth-strava",
|
|
Value: providers.DefaultStravaScopes,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_STRAVA_SCOPE"),
|
|
},
|
|
|
|
// twitter
|
|
&cli.BoolFlag{ //nolint: exhaustruct
|
|
Name: flagTwitterEnabled,
|
|
Usage: "Enable Twitter OAuth provider",
|
|
Category: "oauth-twitter",
|
|
Value: false,
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_TWITTER_ENABLED"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagTwitterConsumerKey,
|
|
Usage: "Twitter OAuth consumer key",
|
|
Category: "oauth-twitter",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_TWITTER_CONSUMER_KEY"),
|
|
},
|
|
&cli.StringFlag{ //nolint: exhaustruct
|
|
Name: flagTwitterConsumerSecret,
|
|
Usage: "Twitter OAuth consumer secret",
|
|
Category: "oauth-twitter",
|
|
Sources: cli.EnvVars("AUTH_PROVIDER_TWITTER_CONSUMER_SECRET"),
|
|
},
|
|
},
|
|
Action: serve,
|
|
}
|
|
}
|
|
|
|
func getRateLimiter(cmd *cli.Command, logger *slog.Logger) gin.HandlerFunc {
|
|
var store ratelimit.Store
|
|
if cmd.String(flagRateLimitMemcacheServer) != "" {
|
|
store = ratelimit.NewMemcacheStore(
|
|
memcache.New(cmd.String(flagRateLimitMemcacheServer)),
|
|
cmd.String(flagRateLimitMemcachePrefix),
|
|
logger.WithGroup("rate-limit-memcache"),
|
|
)
|
|
} else {
|
|
store = ratelimit.NewInMemoryStore()
|
|
}
|
|
|
|
return ratelimit.RateLimit(
|
|
cmd.String(flagAPIPrefix),
|
|
cmd.Int(flagRateLimitGlobalBurst),
|
|
cmd.Duration(flagRateLimitGlobalInterval),
|
|
cmd.Int(flagRateLimitEmailBurst),
|
|
cmd.Duration(flagRateLimitEmailInterval),
|
|
cmd.Bool(flagRateLimitEmailIsGlobal),
|
|
cmd.Bool(flagEmailSigninEmailVerifiedRequired),
|
|
cmd.Int(flagRateLimitSMSBurst),
|
|
cmd.Duration(flagRateLimitSMSInterval),
|
|
cmd.Int(flagRateLimitBruteForceBurst),
|
|
cmd.Duration(flagRateLimitBruteForceInterval),
|
|
cmd.Int(flagRateLimitSignupsBurst),
|
|
cmd.Duration(flagRateLimitSignupsInterval),
|
|
store,
|
|
)
|
|
}
|
|
|
|
func getDependencies( //nolint:ireturn
|
|
ctx context.Context, cmd *cli.Command, db *sql.Queries, logger *slog.Logger,
|
|
) (
|
|
controller.Emailer,
|
|
controller.SMSer,
|
|
*controller.JWTGetter,
|
|
*oidc.IDTokenValidatorProviders,
|
|
error,
|
|
) {
|
|
emailer, templates, err := getEmailer(cmd, logger)
|
|
if err != nil {
|
|
return nil, nil, nil, nil, fmt.Errorf("problem creating emailer: %w", err)
|
|
}
|
|
|
|
sms, err := getSMS(cmd, templates, db, logger)
|
|
if err != nil {
|
|
return nil, nil, nil, nil, fmt.Errorf("problem creating SMS client: %w", err)
|
|
}
|
|
|
|
jwtGetter, err := getJWTGetter(cmd, db)
|
|
if err != nil {
|
|
return nil, nil, nil, nil, fmt.Errorf("problem creating jwt getter: %w", err)
|
|
}
|
|
|
|
idTokenValidator, err := oidc.NewIDTokenValidatorProviders(
|
|
ctx,
|
|
cmd.String(flagAppleAudience),
|
|
cmd.String(flagGoogleAudience),
|
|
"",
|
|
)
|
|
if err != nil {
|
|
return nil, nil, nil, nil, fmt.Errorf("error creating id token validator: %w", err)
|
|
}
|
|
|
|
return emailer, sms, jwtGetter, idTokenValidator, nil
|
|
}
|
|
|
|
func getCORSOptions() oapimw.CORSOptions {
|
|
return oapimw.CORSOptions{
|
|
AllowedOrigins: []string{"*"},
|
|
AllowedMethods: []string{"POST", "GET"},
|
|
AllowedHeaders: nil,
|
|
ExposedHeaders: []string{},
|
|
AllowCredentials: true,
|
|
MaxAge: "86400",
|
|
}
|
|
}
|
|
|
|
func getGoServer(
|
|
ctx context.Context,
|
|
cmd *cli.Command,
|
|
db *sql.Queries,
|
|
encrypter *crypto.Encrypter,
|
|
logger *slog.Logger,
|
|
) (*http.Server, error) {
|
|
ctrl, jwtGetter, err := getController(ctx, cmd, db, encrypter, logger)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
handler := api.NewStrictHandler(ctrl, []api.StrictMiddlewareFunc{})
|
|
|
|
router, mw, err := oapi.NewRouter( //nolint:contextcheck
|
|
docs.OpenAPISchema,
|
|
cmd.String(flagAPIPrefix),
|
|
jwtGetter.MiddlewareFunc,
|
|
getCORSOptions(),
|
|
logger,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create router: %w", err)
|
|
}
|
|
|
|
api.RegisterHandlersWithOptions(
|
|
router,
|
|
handler,
|
|
api.GinServerOptions{
|
|
BaseURL: cmd.String(flagAPIPrefix),
|
|
Middlewares: []api.MiddlewareFunc{mw},
|
|
ErrorHandler: nil,
|
|
},
|
|
)
|
|
|
|
if cmd.Bool(flagRateLimitEnable) {
|
|
router.Use(getRateLimiter(cmd, logger)) //nolint:contextcheck
|
|
}
|
|
|
|
if cmd.String(flagTurnstileSecret) != "" {
|
|
router.Use(middleware.Tunrstile( //nolint:contextcheck
|
|
cmd.String(flagTurnstileSecret), cmd.String(flagAPIPrefix),
|
|
))
|
|
}
|
|
|
|
if cmd.Bool(flagEnableChangeEnv) {
|
|
router.POST(cmd.String(flagAPIPrefix)+"/change-env", ctrl.PostChangeEnv)
|
|
}
|
|
|
|
// for backwards compatibility we keep these two endpoints without the prefix
|
|
if cmd.String(flagAPIPrefix) != "" {
|
|
router.GET("/healthz", func(c *gin.Context) {
|
|
c.JSON(http.StatusOK, gin.H{"status": "ok"})
|
|
})
|
|
router.HEAD("/healthz", func(c *gin.Context) {
|
|
c.Status(http.StatusOK)
|
|
})
|
|
}
|
|
|
|
server := &http.Server{ //nolint:exhaustruct
|
|
Addr: ":" + cmd.String(flagPort),
|
|
Handler: router,
|
|
ReadHeaderTimeout: 5 * time.Second, //nolint:mnd
|
|
}
|
|
|
|
return server, nil
|
|
}
|
|
|
|
func getController(
|
|
ctx context.Context,
|
|
cmd *cli.Command,
|
|
db *sql.Queries,
|
|
encrypter *crypto.Encrypter,
|
|
logger *slog.Logger,
|
|
) (*controller.Controller, *controller.JWTGetter, error) {
|
|
config, err := getConfig(cmd)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("problem creating config: %w", err)
|
|
}
|
|
|
|
emailer, smsClient, jwtGetter, idTokenValidator, err := getDependencies(ctx, cmd, db, logger)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
oauthProviders, err := getOauth2Providers(ctx, cmd, logger)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("problem creating oauth providers: %w", err)
|
|
}
|
|
|
|
ctrl, err := controller.New(
|
|
db,
|
|
config,
|
|
jwtGetter,
|
|
emailer,
|
|
smsClient,
|
|
hibp.NewClient(),
|
|
oauthProviders,
|
|
idTokenValidator,
|
|
controller.NewTotp(cmd.String(flagMfaTotpIssuer), time.Now),
|
|
encrypter,
|
|
cmd.Root().Version,
|
|
)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to create controller: %w", err)
|
|
}
|
|
|
|
return ctrl, jwtGetter, nil
|
|
}
|
|
|
|
func serve(ctx context.Context, cmd *cli.Command) error {
|
|
logger := getLogger(cmd.Bool(flagDebug), cmd.Bool(flagLogFormatTEXT))
|
|
logger.InfoContext(ctx, cmd.Root().Name+" v"+cmd.Root().Version)
|
|
logFlags(ctx, logger, cmd)
|
|
|
|
servCtx, cancel := context.WithCancel(ctx)
|
|
defer cancel()
|
|
|
|
pool, err := getDBPool(ctx, cmd)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create database pool: %w", err)
|
|
}
|
|
defer pool.Close()
|
|
|
|
encrypter, err := crypto.NewEncrypterFromString(cmd.String(flagEncryptionKey))
|
|
if err != nil {
|
|
return fmt.Errorf("problem creating encrypter: %w", err)
|
|
}
|
|
|
|
db := sql.New(pool)
|
|
if err := applyMigrations(servCtx, cmd, db, encrypter, logger); err != nil {
|
|
return fmt.Errorf("failed to apply migrations: %w", err)
|
|
}
|
|
|
|
server, err := getGoServer(ctx, cmd, db, encrypter, logger)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create server: %w", err)
|
|
}
|
|
|
|
go func() {
|
|
defer cancel()
|
|
|
|
logger.InfoContext(
|
|
ctx, "starting server", slog.String("port", cmd.String(flagPort)))
|
|
|
|
if err := server.ListenAndServe(); err != nil {
|
|
logger.ErrorContext(ctx, "server failed", slog.String("error", err.Error()))
|
|
}
|
|
}()
|
|
|
|
<-servCtx.Done()
|
|
|
|
logger.InfoContext(ctx, "shutting down server")
|
|
|
|
if err := server.Shutdown(ctx); err != nil {
|
|
return fmt.Errorf("failed to shutdown server: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|