DEV Community

Divya Darshana
Divya Darshana

Posted on

Authentication vs Authorization: Behind The Scenes

Meet Alex, a software developer who frequently uses different applications throughout the workday. Let's follow Alex's journey as they navigate through various authentication and authorization systems.

Understanding the Fundamentals

Before diving into Alex's experience, let's clarify two critical security concepts:

  • Authentication (AuthN): Verifies identity ("Who are you?")
  • Authorization (AuthZ): Determines permissions ("What can you do?")

Scenario 1: Basic Authentication with Company Wiki

Alex needs to access the internal company wiki to look up documentation.

User Journey:

  1. Alex opens the company wiki in the browser
  2. A login prompt appears requesting username and password
  3. Alex enters company credentials: alex_dev and password CompanySecret123
  4. The browser encodes these credentials as Base64: YWxleF9kZXY6Q29tcGFueVNlY3JldDEyMw==
  5. The request includes the header: Authorization: Basic YWxleF9kZXY6Q29tcGFueVNlY3JldDEyMw==
  6. The server decodes and validates these credentials against the company database
  7. Upon successful validation, Alex is granted access to the wiki

Behind the Scenes (Using GoFr):

// Wiki service using predefined credentials
wikiApp.EnableBasicAuth("alex_dev", "CompanySecret123")

// Or with custom validation against company directory
wikiApp.EnableBasicAuthWithValidator(func(c *container.Container, username, password string) bool {
    return companyDirectory.ValidateCredentials(username, password)
})

// Access authenticated username in handlers
func handleWikiPage(c gofr.Context) (interface{}, error) {
    // Retrieve username from the authenticated request
    username := c.GetAuthInfo().GetUsername()

    log.Printf("User %s accessed the wiki", username)
    return getWikiContent(), nil
}
Enter fullscreen mode Exit fullscreen mode

Security Implications:

  • Authentication: Alex's identity is verified
  • Authorization: Limited - all authenticated users typically receive the same level of access
  • ⚠️ Weakness: Credentials sent with every request (even if encoded)

Scenario 2: API Keys for Development Tools

Later that day, Alex needs to use an internal code analysis tool that requires machine-to-machine authentication.

User Journey:

  1. Alex generates an API key through the company developer portal
  2. The portal displays the key: dev-api-12345-alex-abcdefg
  3. Alex configures the code analysis tool with this key
  4. When Alex runs the tool, it automatically includes the header: Authorization: APIKEY dev-api-12345-alex-abcdefg
  5. The analysis service validates the key and processes Alex's code

Behind the Scenes (Using GoFr):

// Analysis service with API key validation
analysisApp.EnableAPIKeyAuthWithValidator(func(c *container.Container, key string) bool {
    // Check against database of valid keys
    return apiKeyStore.IsValid(key)
})

// Using API key in handlers to track usage
func processCodeAnalysis(c gofr.Context) (interface{}, error) {
    // Retrieve the API key from the authenticated request
    apiKey := c.GetAuthInfo().GetAPIKey()

    // Use API key for tracking or rate limiting
    usageTracker.RecordAPIUsage(apiKey)
    return analyzeCode(c), nil
}
Enter fullscreen mode Exit fullscreen mode

Security Implications:

  • Authentication: The service identifies Alex's request through the key
  • Authorization: Moderate - keys can be scoped to specific services but typically grant uniform access
  • 🔄 Key Advantage: Can be easily rotated if compromised

Scenario 3: OAuth 2.0 & JWT for Cloud Applications

In the afternoon, Alex needs to access the company's cloud-based project management system.

User Journey:

  1. Alex navigates to the project management dashboard
  2. Alex clicks "Login with Company SSO"
  3. The app redirects to the company identity provider login page
  4. Alex enters credentials and completes multi-factor authentication
  5. The identity provider redirects back to the dashboard with an authorization code
  6. The dashboard exchanges this code for a JWT token
  7. The token contains claims about Alex's identity and project access rights
  8. The dashboard displays only the projects Alex is authorized to view

Detailed OAuth 2.0 Flow:

Image description

Behind the Scenes (Using GoFr):

// Configure OAuth with JWKS endpoint for signature validation
dashboardApp.EnableOAuth("https://company-idp/.well-known/jwks.json", 60)

// API handling authorized requests
func handleProjectData(c *gofr.Context) (interface{}, error) {
    // Access JWT claims directly from the context
    claims := c.GetAuthInfo().GetClaims()

    // Extract user information from claims
    userID := claims["sub"].(string)
    roles := claims["roles"].([]interface{})
    projectAccess := claims["projects"].([]interface{})

    // Get the requested project ID from the request
    projectID := c.Param("projectID")

    // Authorization logic based on token claims
    if !hasProjectAccess(userID, roles, projectAccess, projectID) {
        return nil, gofr.NewError(http.StatusForbidden, "Insufficient permissions")
    }

    return getProjectData(projectID), nil
}
Enter fullscreen mode Exit fullscreen mode

JWT Token Content:

{
  "sub": "alex_dev",
  "name": "Alex Developer",
  "email": "alex@company.com",
  "roles": ["developer", "project-viewer"],
  "projects": ["project-123", "project-456"],
  "exp": 1646127125,
  "iss": "https://company-idp/"
}
Enter fullscreen mode Exit fullscreen mode

Security Implications:

  • Authentication: Strong identity verification through company SSO
  • Authorization: Fine-grained access control through token claims
  • 🔒 Security: Token signatures verified with JWKS
  • ⏱️ Time-Bound: Tokens expire automatically, requiring refresh

Scenario 4: Service-to-Service Communication with Client Credentials

Alex deploys a custom application that needs to communicate with other company services.

System Journey:

  1. Alex's application needs to fetch data from the Orders API
  2. The application authenticates itself using client credentials
  3. It receives a JWT with limited service-level permissions
  4. The application includes this token when calling the Orders API
  5. The Orders API validates the token and authorizes the specific operation

Behind the Scenes (Using GoFr):

// Alex's app configuration for secure service communication
app.AddHTTPService("orders", "https://orders-api.internal", &service.OAuthConfig{
    ClientID:     "alex-app-123",
    ClientSecret: "service-secret-xyz",
    TokenURL:     "https://company-idp/oauth/token",
    Scopes:       []string{"read:orders", "write:orders"},
    EndpointParams: map[string][]string{
        "audience": {"https://company-idp/api/v2/"},
    },
})

// Usage in application code
func fetchOrderDetails(c gofr.Context, orderID string) (*Order, error) {
    // Framework handles authentication automatically
    return c.GetHTTPService("orders").Get("/orders/"+orderID, nil)
}
Enter fullscreen mode Exit fullscreen mode

Security Implications:

  • Authentication: Service identity verified through client credentials
  • Authorization: Specific permissions defined through scopes
  • 🔄 Automation: Token management handled by framework
  • 🛡️ Isolation: Services operate with least-privilege access

Working with Authentication in GoFr

GoFr provides a unified interface for accessing authentication information through the GetAuthInfo() method on the context. This powerful abstraction allows developers to write authentication-agnostic code that works regardless of the authentication method in use.

The GetAuthInfo Interface

// Authentication information retrieval methods
type AuthInfo interface {
    // Returns JWT claims when OAuth is enabled
    GetClaims() jwt.MapClaims

    // Returns the username when Basic Auth is enabled
    GetUsername() string

    // Returns the API key when API Key auth is enabled
    GetAPIKey() string
}
Enter fullscreen mode Exit fullscreen mode

Benefits of the Unified Interface

  1. Consistent Access Pattern: Access authentication information with the same method regardless of the auth type
  2. Simplified Handler Code: No need to parse headers or tokens manually
  3. Auth Method Agnostic: Change authentication methods without rewriting handler logic
  4. Type Safety: Methods return appropriate types for each auth method

Example: Authentication-Agnostic Audit Logging

func auditLogger(c *gofr.Context) {
    // Try each authentication method
    authInfo := c.GetAuthInfo()

    var userIdentifier, action, resource string

    // Check for JWT claims first
    if claims := authInfo.GetClaims(); len(claims) > 0 {
        userIdentifier = claims["sub"].(string)
    } else if username := authInfo.GetUsername(); username != "" {
        // Fall back to Basic Auth username
        userIdentifier = username
    } else if apiKey := authInfo.GetAPIKey(); apiKey != "" {
        // Fall back to API key
        userIdentifier = "api:" + apiKey
    } else {
        userIdentifier = "anonymous"
    }

    // Log the action
    c.logf("AUDIT: User %s performed %s on %s", userIdentifier, action, resource)
}
Enter fullscreen mode Exit fullscreen mode

Best Practices for Implementation

Selecting the Right Authentication Method

Scenario Recommended Method Why
Internal tools with limited users Basic Auth Simple implementation for controlled environments
Developer tools and API access API Keys Easy to manage and rotate for individual access
User-facing applications OAuth 2.0 + JWT Secure delegation with rich identity information
Microservice communication Client Credentials Automated, secure service-to-service authentication

Authentication Implementation Checklist

  1. Assess Security Requirements:

    • What is the sensitivity of the protected data?
    • Who are the users and what are their access patterns?
    • What are the compliance requirements?
  2. Layer Your Approach:

    • Use OAuth 2.0 for initial user authentication
    • Implement fine-grained authorization with JWT claims
    • Consider API keys for persistent machine access
  3. Implement Token Validation:

    • Always verify token signatures using JWKS
    • Validate all claims including expiration and issuer
    • Implement token refresh strategies for continuous access
  4. Monitor and Audit:

    • Log authentication events for security analysis
    • Implement anomaly detection for suspicious patterns
    • Regularly rotate secrets and keys

Conclusion

As we've seen through Alex's day, different authentication and authorization methods serve different purposes:

  • Basic Authentication provides simple identity verification for internal tools
  • API Keys offer easy machine-to-machine authentication for development
  • OAuth 2.0 with JWT delivers comprehensive identity and access management
  • Client Credentials enable secure service communication

The GoFr framework simplifies these implementations, providing built-in support for each method while maintaining consistent security practices. GoFr is an open-source framework, and you can explore and contribute to its code on GitHub. If you find it useful, don’t forget to give it a star! ⭐

Remember that effective security requires both proper authentication to verify identity and thorough authorization to enforce access controls. By understanding these concepts and implementing them appropriately, you can create secure applications that protect resources while providing a smooth user experience.

AWS GenAI LIVE image

Real challenges. Real solutions. Real talk.

From technical discussions to philosophical debates, AWS and AWS Partners examine the impact and evolution of gen AI.

Learn more

Top comments (0)

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay