mirror of
https://github.com/warmbly/warmbly.git
synced 2026-09-06 00:01:24 +00:00
148 lines
6.0 KiB
Go
148 lines
6.0 KiB
Go
package models
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// OAuth2 authorization server domain types. Apps register as OAuth clients;
|
|
// users grant them scoped access via the authorization-code flow (client secret
|
|
// required, PKCE optional); the issued access token carries an API-permission
|
|
// bitmask (Scopes) and authenticates API calls through the same gates as an API key.
|
|
|
|
type OAuthAppStatus string
|
|
|
|
const (
|
|
OAuthAppActive OAuthAppStatus = "active"
|
|
OAuthAppDisabled OAuthAppStatus = "disabled"
|
|
)
|
|
|
|
// Credential prefixes mirror the api_keys `wmbly_` convention so a leaked token
|
|
// is greppable and self-describing.
|
|
const (
|
|
OAuthClientIDPrefix = "wmcid_"
|
|
OAuthClientSecretPrefix = "wmcs_"
|
|
OAuthAccessTokenPrefix = "wmat_"
|
|
OAuthRefreshTokenPrefix = "wmrt_"
|
|
OAuthCodePrefix = "wmac_"
|
|
)
|
|
|
|
// Lifetimes. The authorization code is single-use and short; access tokens are
|
|
// short-lived; refresh tokens are long-lived and rotate on every exchange.
|
|
const (
|
|
OAuthAuthorizationCodeTTL = 10 * time.Minute
|
|
OAuthAccessTokenTTL = time.Hour
|
|
OAuthRefreshTokenTTL = 90 * 24 * time.Hour
|
|
)
|
|
|
|
// OAuthApplication is a registered third-party OAuth client.
|
|
type OAuthApplication struct {
|
|
ID uuid.UUID `json:"id"`
|
|
OrganizationID uuid.UUID `json:"organization_id"`
|
|
CreatedBy uuid.UUID `json:"created_by"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
LogoURL string `json:"logo_url"`
|
|
WebsiteURL string `json:"website_url"`
|
|
ClientID string `json:"client_id"`
|
|
ClientSecretHash string `json:"-"`
|
|
RedirectURIs []string `json:"redirect_uris"`
|
|
// AllowedWebhookDomains constrains the host of any webhook endpoint this app
|
|
// registers (subdomain-inclusive: ".acme.com" matches subdomains, "acme.com"
|
|
// is exact). Empty = the app cannot register webhooks.
|
|
AllowedWebhookDomains []string `json:"allowed_webhook_domains"`
|
|
// App-level webhook subscription (the GitHub/Slack-app model). When WebhookURL
|
|
// is set, every org that authorizes this app automatically receives the
|
|
// subscribed events at WebhookURL, scoped to the permissions that org granted,
|
|
// signed with WebhookSecret. WebhookEvents empty = all non-firehose events the
|
|
// grant's scopes allow. The URL host must be within AllowedWebhookDomains.
|
|
WebhookURL string `json:"webhook_url"`
|
|
WebhookEvents []string `json:"webhook_events"`
|
|
WebhookSecret string `json:"-"`
|
|
Scopes uint64 `json:"scopes"`
|
|
Status OAuthAppStatus `json:"status"`
|
|
// IsPublic marks a client that authenticates with PKCE and no secret (native
|
|
// apps, and every MCP client registered via Dynamic Client Registration). The
|
|
// token endpoint accepts no secret for these and PKCE is mandatory.
|
|
IsPublic bool `json:"is_public"`
|
|
// DynamicallyRegistered is true for clients created at runtime via the RFC 7591
|
|
// registration endpoint (no owning org/user until a human grants access).
|
|
DynamicallyRegistered bool `json:"dynamically_registered"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
// OAuthApplicationWithSecret is returned exactly once, on create or secret
|
|
// rotation; the plaintext secret is never stored or shown again.
|
|
type OAuthApplicationWithSecret struct {
|
|
OAuthApplication
|
|
ClientSecret string `json:"client_secret,omitempty"`
|
|
}
|
|
|
|
// OAuthApplicationWrite is the create/update payload from the developer UI.
|
|
// WebhookSecret is server-generated (returned once on set/rotate), never set here.
|
|
type OAuthApplicationWrite struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
LogoURL string `json:"logo_url"`
|
|
WebsiteURL string `json:"website_url"`
|
|
RedirectURIs []string `json:"redirect_uris"`
|
|
AllowedWebhookDomains []string `json:"allowed_webhook_domains"`
|
|
WebhookURL string `json:"webhook_url"`
|
|
WebhookEvents []string `json:"webhook_events"`
|
|
Scopes uint64 `json:"scopes"`
|
|
}
|
|
|
|
// OAuthGrantOrg is one org that has an active grant for an app, with the union
|
|
// of scopes that org granted (across its users). Drives app-webhook materialization.
|
|
type OAuthGrantOrg struct {
|
|
OrgID uuid.UUID
|
|
Scopes uint64
|
|
}
|
|
|
|
// OAuthAuthorizationCode is a single-use code bound to a PKCE challenge and the
|
|
// exact scopes/redirect the user consented to.
|
|
type OAuthAuthorizationCode struct {
|
|
ID uuid.UUID
|
|
CodeHash string
|
|
ApplicationID uuid.UUID
|
|
OrganizationID uuid.UUID
|
|
UserID uuid.UUID
|
|
RedirectURI string
|
|
Scopes uint64
|
|
CodeChallenge string
|
|
CodeChallengeMethod string
|
|
UsedAt *time.Time
|
|
ExpiresAt time.Time
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
// OAuthAccessGrant is an issued access+refresh token pair (tokens stored hashed).
|
|
type OAuthAccessGrant struct {
|
|
ID uuid.UUID `json:"id"`
|
|
ApplicationID uuid.UUID `json:"application_id"`
|
|
OrganizationID uuid.UUID `json:"organization_id"`
|
|
UserID uuid.UUID `json:"user_id"`
|
|
Scopes uint64 `json:"scopes"`
|
|
AccessTokenHash string `json:"-"`
|
|
RefreshTokenHash string `json:"-"`
|
|
AccessExpiresAt time.Time `json:"access_expires_at"`
|
|
RefreshExpiresAt *time.Time `json:"refresh_expires_at,omitempty"`
|
|
RevokedAt *time.Time `json:"revoked_at,omitempty"`
|
|
LastUsedAt *time.Time `json:"last_used_at,omitempty"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
}
|
|
|
|
// OAuthAuthorizedApp is one row in a user's "apps you've authorized" list (a
|
|
// grant joined to its application's display fields).
|
|
type OAuthAuthorizedApp struct {
|
|
ApplicationID uuid.UUID `json:"application_id"`
|
|
Name string `json:"name"`
|
|
LogoURL string `json:"logo_url"`
|
|
WebsiteURL string `json:"website_url"`
|
|
Scopes uint64 `json:"scopes"`
|
|
AuthorizedAt time.Time `json:"authorized_at"`
|
|
LastUsedAt *time.Time `json:"last_used_at,omitempty"`
|
|
}
|