Advanced API Security and OAuth 2.0/OIDC Implementation: Enterprise Identity Protection Framework
Modern enterprise APIs require robust security mechanisms that provide fine-grained access control while maintaining scalability and user experience. This comprehensive guide covers advanced API security implementations using OAuth 2.0 and OpenID Connect, including secure authentication flows, token lifecycle management, and enterprise-grade protection strategies for distributed systems.
Advanced API Security and OAuth 2.0/OIDC Implementation
Section 1: OAuth 2.0 and OIDC Security Architecture
OAuth 2.0 provides authorization framework while OpenID Connect adds authentication layer, together forming the foundation for modern API security implementations.
Enterprise OAuth 2.0 Authorization Server
// oauth-server.go
package main
import (
"context"
"crypto/rsa"
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
)
type OAuth2Server struct {
clients ClientStore
tokens TokenStore
users UserStore
signingKey *rsa.PrivateKey
verifyingKey *rsa.PublicKey
tokenGenerator TokenGenerator
flows map[string]AuthorizationFlow
}
type Client struct {
ID string `json:"client_id"`
Secret string `json:"client_secret,omitempty"`
Name string `json:"client_name"`
RedirectURIs []string `json:"redirect_uris"`
GrantTypes []string `json:"grant_types"`
ResponseTypes []string `json:"response_types"`
Scopes []string `json:"scope"`
TokenEndpointAuthMethod string `json:"token_endpoint_auth_method"`
JWKsURI string `json:"jwks_uri,omitempty"`
PublicKey string `json:"public_key,omitempty"`
Confidential bool `json:"confidential"`
Trusted bool `json:"trusted"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type AccessToken struct {
Value string `json:"access_token"`
Type string `json:"token_type"`
ExpiresIn int64 `json:"expires_in"`
ExpiresAt time.Time `json:"expires_at"`
Scope string `json:"scope"`
ClientID string `json:"client_id"`
UserID string `json:"user_id,omitempty"`
JTI string `json:"jti"`
Audience []string `json:"aud"`
Issuer string `json:"iss"`
Subject string `json:"sub"`
}
type AuthorizationCode struct {
Code string `json:"code"`
ClientID string `json:"client_id"`
UserID string `json:"user_id"`
RedirectURI string `json:"redirect_uri"`
Scope string `json:"scope"`
Challenge string `json:"code_challenge,omitempty"`
ChallengeMethod string `json:"code_challenge_method,omitempty"`
ExpiresAt time.Time `json:"expires_at"`
Used bool `json:"used"`
}
type TokenRequest struct {
GrantType string `form:"grant_type" json:"grant_type"`
Code string `form:"code" json:"code,omitempty"`
RedirectURI string `form:"redirect_uri" json:"redirect_uri,omitempty"`
ClientID string `form:"client_id" json:"client_id"`
ClientSecret string `form:"client_secret" json:"client_secret,omitempty"`
Username string `form:"username" json:"username,omitempty"`
Password string `form:"password" json:"password,omitempty"`
Scope string `form:"scope" json:"scope,omitempty"`
RefreshToken string `form:"refresh_token" json:"refresh_token,omitempty"`
CodeVerifier string `form:"code_verifier" json:"code_verifier,omitempty"`
}
// Authorization Code Flow with PKCE
type AuthorizationCodeFlow struct {
server *OAuth2Server
}
func (acf *AuthorizationCodeFlow) HandleAuthorize(w http.ResponseWriter, r *http.Request) {
params := r.URL.Query()
clientID := params.Get("client_id")
redirectURI := params.Get("redirect_uri")
scope := params.Get("scope")
state := params.Get("state")
codeChallenge := params.Get("code_challenge")
codeChallengeMethod := params.Get("code_challenge_method")
// Validate client
client, err := acf.server.clients.GetClient(clientID)
if err != nil {
http.Error(w, "Invalid client", http.StatusBadRequest)
return
}
// Validate redirect URI
if !acf.validateRedirectURI(client, redirectURI) {
http.Error(w, "Invalid redirect URI", http.StatusBadRequest)
return
}
// Validate PKCE parameters
if client.Confidential && codeChallenge == "" {
acf.redirectWithError(w, redirectURI, "invalid_request", "code_challenge required", state)
return
}
// Check if user is authenticated
userID := acf.getUserFromSession(r)
if userID == "" {
// Redirect to login
acf.redirectToLogin(w, r, clientID, redirectURI, scope, state, codeChallenge, codeChallengeMethod)
return
}
// Check user consent
if !acf.hasValidConsent(userID, clientID, scope) {
// Show consent screen
acf.showConsentScreen(w, r, client, scope, userID)
return
}
// Generate authorization code
code := &AuthorizationCode{\n Code: generateSecureCode(),\n ClientID: clientID,\n UserID: userID,\n RedirectURI: redirectURI,\n Scope: scope,\n Challenge: codeChallenge,\n ChallengeMethod: codeChallengeMethod,\n ExpiresAt: time.Now().Add(10 * time.Minute),\n }\n \n if err := acf.server.tokens.StoreAuthorizationCode(code); err != nil {\n http.Error(w, \"Internal server error\", http.StatusInternalServerError)\n return\n }\n \n // Redirect with authorization code\n acf.redirectWithCode(w, redirectURI, code.Code, state)\n}\n\nfunc (acf *AuthorizationCodeFlow) HandleToken(w http.ResponseWriter, r *http.Request) {\n var req TokenRequest\n if err := json.NewDecoder(r.Body).Decode(&req); err != nil {\n acf.tokenError(w, \"invalid_request\", \"Invalid request format\")\n return\n }\n \n // Authenticate client\n client, err := acf.authenticateClient(req.ClientID, req.ClientSecret, r)\n if err != nil {\n acf.tokenError(w, \"invalid_client\", \"Client authentication failed\")\n return\n }\n \n switch req.GrantType {\n case \"authorization_code\":\n acf.handleAuthorizationCodeGrant(w, req, client)\n case \"refresh_token\":\n acf.handleRefreshTokenGrant(w, req, client)\n case \"client_credentials\":\n acf.handleClientCredentialsGrant(w, req, client)\n default:\n acf.tokenError(w, \"unsupported_grant_type\", \"Grant type not supported\")\n }\n}\n\nfunc (acf *AuthorizationCodeFlow) handleAuthorizationCodeGrant(w http.ResponseWriter, req TokenRequest, client *Client) {\n // Validate authorization code\n code, err := acf.server.tokens.GetAuthorizationCode(req.Code)\n if err != nil || code.Used || time.Now().After(code.ExpiresAt) {\n acf.tokenError(w, \"invalid_grant\", \"Invalid authorization code\")\n return\n }\n \n // Validate client ID\n if code.ClientID != client.ID {\n acf.tokenError(w, \"invalid_grant\", \"Client ID mismatch\")\n return\n }\n \n // Validate redirect URI\n if code.RedirectURI != req.RedirectURI {\n acf.tokenError(w, \"invalid_grant\", \"Redirect URI mismatch\")\n return\n }\n \n // Validate PKCE\n if code.Challenge != \"\" {\n if !acf.validatePKCE(code.Challenge, code.ChallengeMethod, req.CodeVerifier) {\n acf.tokenError(w, \"invalid_grant\", \"PKCE validation failed\")\n return\n }\n }\n \n // Mark code as used\n code.Used = true\n acf.server.tokens.UpdateAuthorizationCode(code)\n \n // Generate access token\n accessToken := acf.generateAccessToken(client, code.UserID, code.Scope)\n refreshToken := acf.generateRefreshToken(client, code.UserID, code.Scope)\n \n response := map[string]interface{}{\n \"access_token\": accessToken.Value,\n \"token_type\": \"Bearer\",\n \"expires_in\": accessToken.ExpiresIn,\n \"refresh_token\": refreshToken,\n \"scope\": accessToken.Scope,\n }\n \n w.Header().Set(\"Content-Type\", \"application/json\")\n w.Header().Set(\"Cache-Control\", \"no-store\")\n w.Header().Set(\"Pragma\", \"no-cache\")\n json.NewEncoder(w).Encode(response)\n}\n\n// OpenID Connect Implementation\ntype OIDCProvider struct {\n oauth2Server *OAuth2Server\n userInfo UserInfoEndpoint\n discovery DiscoveryDocument\n jwks JWKSEndpoint\n}\n\ntype IDToken struct {\n Issuer string `json:\"iss\"`\n Subject string `json:\"sub\"`\n Audience string `json:\"aud\"`\n ExpiresAt int64 `json:\"exp\"`\n IssuedAt int64 `json:\"iat\"`\n AuthTime int64 `json:\"auth_time,omitempty\"`\n Nonce string `json:\"nonce,omitempty\"`\n Email string `json:\"email,omitempty\"`\n EmailVerified bool `json:\"email_verified,omitempty\"`\n Name string `json:\"name,omitempty\"`\n PreferredUsername string `json:\"preferred_username,omitempty\"`\n Groups []string `json:\"groups,omitempty\"`\n}\n\nfunc (oidc *OIDCProvider) GenerateIDToken(user *User, client *Client, nonce string) (string, error) {\n now := time.Now()\n \n claims := IDToken{\n Issuer: \"https://auth.company.com\",\n Subject: user.ID,\n Audience: client.ID,\n ExpiresAt: now.Add(1 * time.Hour).Unix(),\n IssuedAt: now.Unix(),\n AuthTime: user.LastAuthTime.Unix(),\n Nonce: nonce,\n Email: user.Email,\n EmailVerified: user.EmailVerified,\n Name: user.Name,\n PreferredUsername: user.Username,\n Groups: user.Groups,\n }\n \n token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)\n token.Header[\"kid\"] = \"key-1\"\n \n return token.SignedString(oidc.oauth2Server.signingKey)\n}\n\nfunc (oidc *OIDCProvider) HandleUserInfo(w http.ResponseWriter, r *http.Request) {\n // Extract bearer token\n authHeader := r.Header.Get(\"Authorization\")\n if !strings.HasPrefix(authHeader, \"Bearer \") {\n http.Error(w, \"Unauthorized\", http.StatusUnauthorized)\n return\n }\n \n tokenValue := strings.TrimPrefix(authHeader, \"Bearer \")\n \n // Validate access token\n token, err := oidc.oauth2Server.tokens.ValidateAccessToken(tokenValue)\n if err != nil {\n http.Error(w, \"Invalid token\", http.StatusUnauthorized)\n return\n }\n \n // Get user info\n user, err := oidc.oauth2Server.users.GetUser(token.UserID)\n if err != nil {\n http.Error(w, \"User not found\", http.StatusNotFound)\n return\n }\n \n // Build user info response based on scope\n userInfo := oidc.buildUserInfoResponse(user, token.Scope)\n \n w.Header().Set(\"Content-Type\", \"application/json\")\n json.NewEncoder(w).Encode(userInfo)\n}\n\n// API Gateway Integration\ntype APIGateway struct {\n oauth2Server *OAuth2Server\n rateLimiter *RateLimiter\n routes map[string]*Route\n middleware []Middleware\n metrics *MetricsCollector\n}\n\ntype Route struct {\n Path string\n Method string\n Handler http.HandlerFunc\n Scopes []string\n RateLimit *RateLimit\n Timeout time.Duration\n RequireAuth bool\n}\n\ntype Middleware interface {\n Process(next http.HandlerFunc) http.HandlerFunc\n}\n\ntype AuthenticationMiddleware struct {\n oauth2Server *OAuth2Server\n}\n\nfunc (am *AuthenticationMiddleware) Process(next http.HandlerFunc) http.HandlerFunc {\n return func(w http.ResponseWriter, r *http.Request) {\n // Extract token from Authorization header\n authHeader := r.Header.Get(\"Authorization\")\n if authHeader == \"\" {\n http.Error(w, \"Missing authorization header\", http.StatusUnauthorized)\n return\n }\n \n if !strings.HasPrefix(authHeader, \"Bearer \") {\n http.Error(w, \"Invalid authorization header format\", http.StatusUnauthorized)\n return\n }\n \n tokenValue := strings.TrimPrefix(authHeader, \"Bearer \")\n \n // Validate token\n token, err := am.oauth2Server.tokens.ValidateAccessToken(tokenValue)\n if err != nil {\n http.Error(w, \"Invalid or expired token\", http.StatusUnauthorized)\n return\n }\n \n // Add token info to request context\n ctx := context.WithValue(r.Context(), \"token\", token)\n ctx = context.WithValue(ctx, \"user_id\", token.UserID)\n ctx = context.WithValue(ctx, \"client_id\", token.ClientID)\n ctx = context.WithValue(ctx, \"scopes\", strings.Split(token.Scope, \" \"))\n \n next(w, r.WithContext(ctx))\n }\n}\n\ntype AuthorizationMiddleware struct {}\n\nfunc (azm *AuthorizationMiddleware) Process(next http.HandlerFunc) http.HandlerFunc {\n return func(w http.ResponseWriter, r *http.Request) {\n // Get required scopes for this route\n route := getRouteFromContext(r.Context())\n if route == nil || len(route.Scopes) == 0 {\n next(w, r)\n return\n }\n \n // Get user scopes from token\n tokenScopes, ok := r.Context().Value(\"scopes\").([]string)\n if !ok {\n http.Error(w, \"No scopes found in token\", http.StatusForbidden)\n return\n }\n \n // Check if user has required scopes\n if !hasRequiredScopes(tokenScopes, route.Scopes) {\n http.Error(w, \"Insufficient scopes\", http.StatusForbidden)\n return\n }\n \n next(w, r)\n }\n}\n\n// JWT Token Security\ntype JWTSecurity struct {\n signingKey *rsa.PrivateKey\n verifyingKeys map[string]*rsa.PublicKey\n algorithms []string\n leeway time.Duration\n}\n\nfunc NewJWTSecurity() *JWTSecurity {\n return &JWTSecurity{\n verifyingKeys: make(map[string]*rsa.PublicKey),\n algorithms: []string{\"RS256\", \"RS384\", \"RS512\"},\n leeway: 30 * time.Second,\n }\n}\n\nfunc (js *JWTSecurity) ValidateJWT(tokenString string) (*jwt.Token, error) {\n token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {\n // Verify signing method\n if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {\n return nil, fmt.Errorf(\"unexpected signing method: %v\", token.Header[\"alg\"])\n }\n \n // Get key ID from header\n kid, ok := token.Header[\"kid\"].(string)\n if !ok {\n return nil, fmt.Errorf(\"missing key ID in token header\")\n }\n \n // Get verification key\n verifyingKey, exists := js.verifyingKeys[kid]\n if !exists {\n return nil, fmt.Errorf(\"unknown key ID: %s\", kid)\n }\n \n return verifyingKey, nil\n })\n \n if err != nil {\n return nil, err\n }\n \n if !token.Valid {\n return nil, fmt.Errorf(\"invalid token\")\n }\n \n return token, nil\n}\n\n// Advanced Security Features\ntype SecurityEnhancement struct {\n tokenBinding *TokenBinding\n dpop *DPoP\n mtls *MutualTLS\n jarValidation *JARValidation\n}\n\n// Demonstration-of-Proof-of-Possession (DPoP)\ntype DPoP struct {\n allowedAlgorithms []string\n maxSkew time.Duration\n}\n\nfunc (dpop *DPoP) ValidateDPoPProof(proof string, method string, url string, accessToken string) error {\n token, err := jwt.Parse(proof, func(token *jwt.Token) (interface{}, error) {\n // Validate algorithm\n alg, ok := token.Header[\"alg\"].(string)\n if !ok {\n return nil, fmt.Errorf(\"missing algorithm in DPoP proof\")\n }\n \n if !contains(dpop.allowedAlgorithms, alg) {\n return nil, fmt.Errorf(\"unsupported algorithm: %s\", alg)\n }\n \n // Extract public key from JWK\n jwk, ok := token.Header[\"jwk\"]\n if !ok {\n return nil, fmt.Errorf(\"missing JWK in DPoP proof\")\n }\n \n return parseJWKToPublicKey(jwk)\n })\n \n if err != nil {\n return err\n }\n \n claims, ok := token.Claims.(jwt.MapClaims)\n if !ok {\n return fmt.Errorf(\"invalid claims format\")\n }\n \n // Validate claims\n if claims[\"htm\"] != method {\n return fmt.Errorf(\"HTTP method mismatch\")\n }\n \n if claims[\"htu\"] != url {\n return fmt.Errorf(\"HTTP URI mismatch\")\n }\n \n // Validate timestamp\n iat, ok := claims[\"iat\"].(float64)\n if !ok {\n return fmt.Errorf(\"missing issued at claim\")\n }\n \n issuedAt := time.Unix(int64(iat), 0)\n if time.Now().Sub(issuedAt) > dpop.maxSkew {\n return fmt.Errorf(\"DPoP proof too old\")\n }\n \n return nil\n}\n\n// Token Introspection\ntype TokenIntrospection struct {\n oauth2Server *OAuth2Server\n}\n\nfunc (ti *TokenIntrospection) HandleIntrospection(w http.ResponseWriter, r *http.Request) {\n // Authenticate client\n clientID, clientSecret, ok := r.BasicAuth()\n if !ok {\n http.Error(w, \"Unauthorized\", http.StatusUnauthorized)\n return\n }\n \n client, err := ti.oauth2Server.clients.AuthenticateClient(clientID, clientSecret)\n if err != nil {\n http.Error(w, \"Invalid client credentials\", http.StatusUnauthorized)\n return\n }\n \n // Get token from request\n tokenValue := r.FormValue(\"token\")\n if tokenValue == \"\" {\n http.Error(w, \"Missing token parameter\", http.StatusBadRequest)\n return\n }\n \n // Introspect token\n response := ti.introspectToken(tokenValue, client)\n \n w.Header().Set(\"Content-Type\", \"application/json\")\n json.NewEncoder(w).Encode(response)\n}\n\nfunc (ti *TokenIntrospection) introspectToken(tokenValue string, client *Client) map[string]interface{} {\n token, err := ti.oauth2Server.tokens.ValidateAccessToken(tokenValue)\n if err != nil {\n return map[string]interface{}{\"active\": false}\n }\n \n response := map[string]interface{}{\n \"active\": true,\n \"scope\": token.Scope,\n \"client_id\": token.ClientID,\n \"username\": token.UserID,\n \"token_type\": token.Type,\n \"exp\": token.ExpiresAt.Unix(),\n \"iat\": token.ExpiresAt.Add(-time.Duration(token.ExpiresIn)*time.Second).Unix(),\n \"sub\": token.Subject,\n \"aud\": token.Audience,\n \"iss\": token.Issuer,\n \"jti\": token.JTI,\n }\n \n return response\n}\n```\n\nThis comprehensive API security guide provides enterprise-grade OAuth 2.0 and OpenID Connect implementations with advanced security features including PKCE, DPoP, token introspection, and JWT security. Organizations should implement these patterns to ensure robust API protection while maintaining scalability and compliance with modern security standards.