Add OAuth2/OpenID Connect authorization server
Implement a full OAuth2 2.0 and OpenID Connect 1.0 authorization server with support for authorization code flow (with PKCE), refresh token rotation, device authorization grant, dynamic client registration, token introspection, and token revocation. Includes database schema, coredata layer, service logic, HTTP handlers, OIDC discovery endpoint, and JWKS publishing. Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
@@ -31,6 +31,7 @@ import (
|
||||
"go.probo.inc/probo/pkg/esign"
|
||||
"go.probo.inc/probo/pkg/file"
|
||||
"go.probo.inc/probo/pkg/iam"
|
||||
"go.probo.inc/probo/pkg/iam/oauth2server"
|
||||
"go.probo.inc/probo/pkg/mailman"
|
||||
"go.probo.inc/probo/pkg/probo"
|
||||
"go.probo.inc/probo/pkg/securecookie"
|
||||
@@ -41,6 +42,7 @@ import (
|
||||
console_web "go.probo.inc/probo/pkg/server/web"
|
||||
"go.probo.inc/probo/pkg/slack"
|
||||
"go.probo.inc/probo/pkg/trust"
|
||||
"go.probo.inc/probo/pkg/uri"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
@@ -70,7 +72,9 @@ type Server struct {
|
||||
trustWebServer *trust_web.Server
|
||||
router *chi.Mux
|
||||
extraHeaderFields map[string]string
|
||||
baseURL string
|
||||
proboService *probo.Service
|
||||
iamService *iam.Service
|
||||
trustService *trust.Service
|
||||
logger *log.Logger
|
||||
}
|
||||
@@ -119,7 +123,9 @@ func NewServer(cfg Config) (*Server, error) {
|
||||
trustWebServer: trustWebServer,
|
||||
router: router,
|
||||
extraHeaderFields: cfg.ExtraHeaderFields,
|
||||
baseURL: cfg.BaseURL.String(),
|
||||
proboService: cfg.Probo,
|
||||
iamService: cfg.IAM,
|
||||
trustService: cfg.Trust,
|
||||
logger: cfg.Logger,
|
||||
}
|
||||
@@ -130,6 +136,11 @@ func NewServer(cfg Config) (*Server, error) {
|
||||
}
|
||||
|
||||
func (s *Server) setupRoutes(baseURL string) {
|
||||
// OIDC Discovery 1.0 §4 and RFC 8414 §3 both require the metadata
|
||||
// document at the issuer root under well-known paths.
|
||||
s.router.Get("/.well-known/openid-configuration", s.oidcDiscoveryHandler)
|
||||
s.router.Get("/.well-known/oauth-authorization-server", s.oidcDiscoveryHandler)
|
||||
|
||||
s.router.Mount("/api", http.StripPrefix("/api", s.apiServer))
|
||||
s.router.Mount("/mail-actions", http.StripPrefix("/mail-actions", s.mailActionsHandler))
|
||||
|
||||
@@ -153,6 +164,25 @@ func (s *Server) setExtraHeaders(w http.ResponseWriter) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) oidcDiscoveryHandler(w http.ResponseWriter, r *http.Request) {
|
||||
api := s.baseURL + "/api/connect/v1"
|
||||
|
||||
endpoints := oauth2server.Endpoints{
|
||||
Authorization: uri.URI(api + "/oauth2/authorize"),
|
||||
Token: uri.URI(api + "/oauth2/token"),
|
||||
Userinfo: uri.URI(api + "/oauth2/userinfo"),
|
||||
JWKS: uri.URI(api + "/oauth2/jwks"),
|
||||
Registration: uri.URI(api + "/oauth2/register"),
|
||||
Introspection: uri.URI(api + "/oauth2/introspect"),
|
||||
Revocation: uri.URI(api + "/oauth2/revoke"),
|
||||
DeviceAuthorization: uri.URI(api + "/oauth2/device"),
|
||||
}
|
||||
|
||||
metadata := s.iamService.OAuth2ServerService.Metadata(endpoints)
|
||||
w.Header().Set("Cache-Control", "public, max-age=3600")
|
||||
httpserver.RenderJSON(w, http.StatusOK, metadata)
|
||||
}
|
||||
|
||||
func (s *Server) handleCustomDomain404(w http.ResponseWriter, r *http.Request) {
|
||||
httpserver.RenderError(w, http.StatusNotFound, errors.New("not found"))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user