👨‍💻
Application Security Handbook
  • Application Security Handbook
  • Web Application
    • Authentication
      • Authentication with Login and Password
      • Authentication with Phone Number
      • OAuth 2.0 Authentication
      • Multi-factor Authentication
      • Default Passwords
      • Password Change
      • Password Policy
      • Password Reset
      • Password Storage
      • One Time Password (OTP)
      • Email Address Confirmation
    • Authorization
    • Concept of Trusted Devices
    • Content Security Policy (CSP)
    • Cookie Security
    • Cryptography
      • Cryptographic Keys Management
      • Encryption
      • Hash-based Message Authentication Code (HMAC)
      • Hashing
      • Random Generators
      • Universal Unique Identifier (UUID)
    • Error and Exception Handling
    • File Upload
    • Input Validation
    • JSON Web Token (JWT)
    • Logging and Monitoring
    • Output Encoding
    • Regular Expressions
    • Sensitive Data Management
    • Session Management
    • Transport Layer Protection
    • Vulnerability Mitigation
      • Brute-force
      • Command Injection
      • Cross-Site Request Forgery (CSRF)
      • Cross-Site Scripting (XSS)
      • Mass Parameter Assignment
      • Parameter Pollution
      • Path Traversal
      • Regular Expression Denial of Service (ReDoS)
      • SQL Injection (SQLi)
      • XML External Entity (XXE) Injection
Powered by GitBook
On this page
  • Overview
  • General
  • Frameworks & packages for implementation of OAuth2 authentication
  • References
  1. Web Application
  2. Authentication

OAuth 2.0 Authentication

PreviousAuthentication with Phone NumberNextMulti-factor Authentication

Last updated 1 year ago

Overview

This page contains recommendations for the implementation of OAuth 2.0 authentication.

General

  • Do not implement support of OAuth2 authentication from scratch. Instead, use well know and up-to-date frameworks or packages.

  • Use the Authorization Grant Flow to implement OAuth2 authentication.

  • Do not use the Implicit Grant Flow. Disable the use of the Implicit Grant Flow if a framework or package supports the flow.

  • Use URLs from an allow list as redirect_uri.

  • Do not use wildcards for defining redirect_uri.

  • Use a unique allow list with redirect_uri for each OAuth2 client.

  • Generate a unique state parameter for each authentication attempt.

  • Use the state parameter of length 16+ bytes.

  • Generate a unique authorization code for each authentication attempt.

  • Use authorization codes of length 16+ bytes.

  • Set expiration time for an authorization code < 1 hour.

  • Use an authorization code once. Delete an authorization code or transfer it to a final status that prohibits reusing.

  • Do not redirect users to URLs specified in the parameters without checking them against an allow list.

  • Do not assign OAuth authentication for already existing accounts during authentication.

Clarification

If an application automatically assigns OAuth authentication to an existing account during authentication, this means that an attacker could potentially take over a victim's account using the flow like this:

  1. An attacker creates an account in a service provider using a victim's email linked to an account in our application.

  2. An attacker uses Sign in with ... to login into an account in our application.

  3. An application will find a victim's account using an email address from an attacker's service provider account (an attacker linked that email with a service provider account).

  4. Since the emails are the same, an application will log in an attacker to a victim's account.

    • Successful authentication

    • Failed authentication

    • Successful connection to an OAuth provider

    • Failed connection to an OAuth provider

    • Disconnect an OAuth provider

  • Notify a user via an available communication channel (email, push, SMS, etc.) about successful login under their account from an unknown location, browser, client, etc.

Frameworks & packages for implementation of OAuth2 authentication

import (
    "crypto/rand"
    "context"
    "encoding/base64"
    "golang.org/x/oauth2"
    "fmt"
    "log"
    "os"
    "net/http"
    "io/ioutil"
)

var endpoint = oauth2.Endpoint{
    AuthURL: "https://website.local/auth",
    TokenURL: "https://website.local/oauth/access_token",
    AuthStyle: oauth2.AuthStyleInParams,
}

var oauthConfig = &oauth2.Config{
    RedirectURL: "http://localhost:8000/auth/callback",
    ClientID: os.Getenv("OAUTH_CLIENT_ID"),
    ClientSecret: os.Getenv("OAUTH_CLIENT_SECRET"),
    Scopes: []string{"user:email"},
    Endpoint: endpoint,
}

// initiate an authentication process
func oauthLogin(w http.ResponseWriter, r *http.Request) {
    state := generateState()
    u := oauthConfig.AuthCodeURL(state)
    http.Redirect(w, r, u, http.StatusTemporaryRedirect)
}

// exchange code for access token
// use an access token to make requests to a resource server
func oauthCallback(w http.ResponseWriter, r *http.Request) {
    code := r.FormValue("code")
    token, err := oauthConfig.Exchange(context.Background(), code)
    if err != nil {
        log.Println(fmt.Errorf("code exchange wrong: %s", err.Error()))
        http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
        return
    }
    data, err := getUserData(token)
    if err != nil {
        log.Println(err.Error())
        http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
        return
    }
    fmt.Fprintf(w, "UserInfo: %s\n", data)
}

func generateState() string {
    b := make([]byte, 32)
    rand.Read(b)
    state := base64.URLEncoding.EncodeToString(b)
    return state
}

func getUserData(oauth2.Token *token) {
    url := "https://api.resource.server.local/user"
    req, err := http.NewRequest("GET", url, nil)
    req.Header.Add("Authorization", "Bearer " + token.AccessToken)
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return nil, fmt.Errorf("failed getting user data: %s", err.Error())
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return nil, fmt.Errorf("failed read response: %s", err.Error())
    }
    return body, nil
}

References

Implement CSRF protection using the state parameter, see the page.

Generate the state parameter using a cryptographically strong random generator, see the page.

Generate the authorization code using a cryptographically strong random generator, see the page.

Comply with requirements from the page.

Log all authentication decisions (successful and not successful), see the page.

Comply with requirements from the page.

Limit the number of attempts to sign in for a certain period, see the page.

Enforce multi-factor authentication, see the page.

Use the package, that implements an OAuth2 client.

Vulnerability Mitigation: Cross-Site Request Forgery (CSRF)
Cryptography: Random Generators
Cryptography: Random Generators
Error and Exception Handling
Logging and Monitoring
Sensitive Data Management
Vulnerability Mitigation: Brute-force
Authentication: Multi-factor Authentication
oauth2
OWASP Cheat Sheet Series: Authentication Cheat Sheet