caddydiscord

package module
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 29, 2024 License: AGPL-3.0 Imports: 19 Imported by: 0

README

Caddy - Discord Discord

tl;dr: Authenticate caddy routes based on a Discord User Identity.
e.g. Accessing /really-cool-people requires user to have {Role} within {Guild}

This package contains a module allowing authorization in Caddy based on a Discord Identity, by using Discords OAuth2 flow (authorization code grant).


Licensed under GNU Affero General Public License v3.0
Logo by @AutonomousCat


Caddy Modules
caddydiscord
http.authentication.providers.discord
http.handler.discord

Docker (Container)

docker run -p 8080:8080 \
  --rm -v $PWD/Caddyfile:/etc/caddy/Caddyfile \
  enumgg/caddy-discord:v1.0.1

Discord Resources

realm allows you to name a label and group together specific targeted Discord Users by using the directives below.

Resource Description Example
User ID Discord User IDs (optionally with guild presence)
realm godmode {
user 314009111187026172 # Allow user regardless of which guild they are in
guild 1063070451111289907 {
user 314009111187026199 # Allow user if they're part of guild
}
}
Guild Any user that exists within the guild
realm cool_guild_users {
guild 1063070451111289907 {
* # Allows all users
}
}
Role Users that assigned a specific role within a guild
realm cool_role {
guild 1063070451111289907 {
role 106301111332755034
role 106301111332755034
}
}

Loosely inspired from caddy-security's Discord OAuth2 module, with a much stronger focus on coupling Discord and Caddy for authentication purposes.

Install

Download Latest Version

  1. Download caddy + caddy-discord
    • Using released binaries
    • Build yourself using xcaddy
      • xcaddy build --with github.com/enum-gg/caddy-discord
  2. Create Discord Application (Discord Developer Portal)
    • New Application
    • OAuth2
      • Obtain your Client ID & Client secret
      • Add Redirects Docs
  3. Prepare your Caddyfile
    • Gather your Discord App OAuth2 Client ID & Client Secret,
    • Decide your route for caddy-discords to use as the OAuth2

Caddyfile Example

{
    discord {
        client_id 1000000000000000000 # Discord app OAuth client ID 
        client_secret 8CEPZZZZZAfl_w19ZZZZW_k # Discord app OAuth secret
        redirect http://localhost:8080/discord/callback # Route you've configured with `discordauth callback`

        realm clique {
            guild 106307051119907 {
                role 10630111112755034
            }
        }
        
        realm just_for_me {
            user 31400111187026172
        }
    }
}

http://localhost:8080 {
    route /discord/callback {
         # Desigate route as OAuth callback endpoint
         discord callback 
   }

    route /discordians-only {
         # Only allow discord users that auth against 'really_cool_area' realm 
         protect using clique 
        
         respond "Hello {http.auth.user.username}!<br /><br /><img src='https://cdn.discordapp.com/avatars/{http.auth.user.id}/{http.auth.user.avatar}?size=4096.png'> "
    }

    respond "Hello, world!"
}

Building

xcaddy build --with github.com/enum-gg/caddy-discord=./

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewAuthFlowToken

func NewAuthFlowToken(redirectURI string, realm string, exp time.Time) *jwt.Token

func NewAuthedTokenParser

func NewAuthedTokenParser(key []byte) func(signedToken string) (*AuthenticatedClaims, error)

func NewAuthenticatedToken

func NewAuthenticatedToken(identity discord.User, realm string, exp time.Time, authorised bool) *jwt.Token

func NewFlowTokenParser

func NewFlowTokenParser(key []byte) func(signedToken string) (*FlowTokenParser, error)

func ResourceRequiresGuild

func ResourceRequiresGuild(resource DiscordResource) bool

func RoleChecker added in v1.1.1

func RoleChecker(desiredRoleID string, roles []string) string

Types

type AccessIdentifier

type AccessIdentifier struct {
	Resource   DiscordResource `json:"Resource"` // role, user
	Identifier string          `json:"Identifier,omitempty"`
	GuildID    string          `json:"GuildID,omitempty"`
	Wildcard   bool            `json:"Wildcard,omitempty"`
}

type AuthedTokenParserSignature

type AuthedTokenParserSignature = func(signedToken string) (*AuthenticatedClaims, error)

type AuthenticatedClaims

type AuthenticatedClaims struct {
	Realm string `json:"realm,omitempty"`

	Username   string `json:"user,omitempty"`
	Avatar     string `json:"avatar,omitempty"`
	Authorised bool   `json:"authorised,omitempty"`
	jwt.RegisteredClaims
}

func (AuthenticatedClaims) GetAudience

func (a AuthenticatedClaims) GetAudience() string

type CookieNamer added in v1.2.0

type CookieNamer func(string) string

func CookieName added in v1.2.0

func CookieName(executionKey string) CookieNamer

type CustomClaim

type CustomClaim interface {
	jwt.Claims
}

type DiscordAuthPlugin

type DiscordAuthPlugin struct {
	Configuration []string
	OAuth         *oauth2.Config
	Realms        *RealmRegistry
	Key           string
	// contains filtered or unexported fields
}

DiscordAuthPlugin is used in combination with http.authentication.providers.discord to provide an authentication layer based on a Discord identity.

See https://caddyserver.com/docs/modules/http.authentication.providers.discord or https://github.com/enum-gg/caddy-discord

func (DiscordAuthPlugin) CaddyModule

func (DiscordAuthPlugin) CaddyModule() caddy.ModuleInfo

func (*DiscordAuthPlugin) Provision

func (s *DiscordAuthPlugin) Provision(ctx caddy.Context) error

func (DiscordAuthPlugin) ServeHTTP

ServeHTTP implements caddyhttp.MiddlewareHandler.

func (*DiscordAuthPlugin) UnmarshalCaddyfile

func (s *DiscordAuthPlugin) UnmarshalCaddyfile(d *caddyfile.Dispenser) error

UnmarshalCaddyfile will extract discordauth directives on a server-level

route /some/path/callback {
    discordauth callback
}

func (*DiscordAuthPlugin) Validate

func (s *DiscordAuthPlugin) Validate() error

type DiscordPortalApp

type DiscordPortalApp struct {
	// ClientID is the "Client ID" from your Discord Application OAuth information
	ClientID string `json:"clientID"`

	// ClientSecret is the "Client Secret" from your Discord Application
	// OAuth information.
	//
	// Treat this is sensitive. Do not share or expose it to anyone.
	ClientSecret string `json:"clientSecret"`

	// RedirectURL is the destination for clients for the OAuth flow
	// Your Discord Application's OAuth "Redirects" needs to be aware
	// of this endpoint.
	//
	// Within your Caddyfile this URL should be configured with "discord callback".
	RedirectURL string `json:"redirectURL"`

	// Realms group together explicit rules about whom to authorise.
	Realms RealmRegistry `json:"realms"`

	// Key is the signing key used for the JWT stored as the client's cookie
	// it is ephemeral alongside the caddy server process.
	Key string `json:"key,omitempty"`

	// ExecutionKey is an ephemeral identifier for the client's cookie which contains
	// the JWT payload proving Discord identity. It is the 'public' version of the signing Key.
	//
	// End users will have to perform the OAuth flow once uniquely per ExecutionKey,
	//  which will be a touchless experience barely noticeably from their end.
	//
	// ExecutionKey exists as an alternative to the server operator providing their own
	// JWT signing key; which should eventually become an optional configuration.
	ExecutionKey string `json:"signature,omitempty"`
	// contains filtered or unexported fields
}

DiscordPortalApp allows you to authenticate caddy routes based on a Discord User Identity.

e.g. Accessing /really-cool-people requires user to have {Role} within {Guild}

Discord's OAuth flow is used for identity using your own Discord developer application.

See an example Caddyfile https://github.com/enum-gg/caddy-discord#caddyfile-example

func (DiscordPortalApp) CaddyModule

func (DiscordPortalApp) CaddyModule() caddy.ModuleInfo

CaddyModule returns the Caddy module information.

func (*DiscordPortalApp) Provision

func (d *DiscordPortalApp) Provision(_ caddy.Context) error

func (DiscordPortalApp) Start

func (d DiscordPortalApp) Start() error

func (DiscordPortalApp) Stop

func (d DiscordPortalApp) Stop() error

Stop stops the App.

func (DiscordPortalApp) Validate

func (d DiscordPortalApp) Validate() error

type DiscordResource

type DiscordResource int
const (
	Unknown          DiscordResource = 0
	DiscordGuildRule DiscordResource = 1
	DiscordRoleRule  DiscordResource = 2
	// DiscordMemberRule represents a specific Discord User within a specific guild
	DiscordMemberRule DiscordResource = 3
	// DiscordUserRule represents a Discord User Snowflake ID
	DiscordUserRule DiscordResource = 4
)

type FlowTokenParser

type FlowTokenParser struct {
	Realm       string `json:"realm,omitempty"`
	RedirectURI string `json:"redirectURI,omitempty"`
	jwt.RegisteredClaims
}

func (FlowTokenParser) GetAudience

func (f FlowTokenParser) GetAudience() string

type FlowTokenParserSignature

type FlowTokenParserSignature = func(signedToken string) (*FlowTokenParser, error)

type JWTManager

type JWTManager struct {
	// contains filtered or unexported fields
}

type ProtectorPlugin

type ProtectorPlugin struct {
	OAuthConfig *oauth2.Config

	Realm string
	// contains filtered or unexported fields
}

ProtectorPlugin allows you to authenticate caddy routes from a Discord User Identity.

e.g. Accessing /really-cool-people requires user to have {Role} within {Guild}

Discord's OAuth flow is used for identity using your own Discord developer application.

See an example Caddyfile https://github.com/enum-gg/caddy-discord#caddyfile-example

func (*ProtectorPlugin) Authenticate

func (p *ProtectorPlugin) Authenticate(w http.ResponseWriter, r *http.Request) (caddyauth.User, bool, error)

Authenticate implements caddyhttp.MiddlewareHandler.

func (ProtectorPlugin) CaddyModule

func (ProtectorPlugin) CaddyModule() caddy.ModuleInfo

func (*ProtectorPlugin) Provision

func (p *ProtectorPlugin) Provision(ctx caddy.Context) error

func (*ProtectorPlugin) UnmarshalCaddyfile

func (p *ProtectorPlugin) UnmarshalCaddyfile(d *caddyfile.Dispenser) error

func (*ProtectorPlugin) Validate

func (p *ProtectorPlugin) Validate() error

type Realm

type Realm struct {
	Ref         string              `json:"Ref"`
	Identifiers []*AccessIdentifier `json:"Identifiers"`
}

func (Realm) GetAllGuilds

func (r Realm) GetAllGuilds() []string

type RealmBuilder

type RealmBuilder struct {
	// contains filtered or unexported fields
}

func NewRealmBuilder

func NewRealmBuilder() RealmBuilder

func (RealmBuilder) AllowAllDiscordUsers

func (r RealmBuilder) AllowAllDiscordUsers()

func (RealmBuilder) AllowAllGuildMembers

func (r RealmBuilder) AllowAllGuildMembers(guildID string)

func (RealmBuilder) AllowDiscordUser

func (r RealmBuilder) AllowDiscordUser(userID string)

func (RealmBuilder) AllowGuildMember

func (r RealmBuilder) AllowGuildMember(guildID string, userID string)

func (RealmBuilder) AllowGuildRole

func (r RealmBuilder) AllowGuildRole(guildID string, roleID string)

func (RealmBuilder) Build

func (r RealmBuilder) Build() *Realm

func (RealmBuilder) Name

func (r RealmBuilder) Name(name string)

type RealmRegistry

type RealmRegistry []*Realm

func (RealmRegistry) ByName

func (r RealmRegistry) ByName(name string) *Realm

type TokenSignerSignature

type TokenSignerSignature = func(token *jwt.Token) (string, error)

func NewTokenSigner

func NewTokenSigner(key []byte) TokenSignerSignature

Directories

Path Synopsis
cmd
caddy
Package main is the entry point of the Caddy application.
Package main is the entry point of the Caddy application.
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL