[Go] Try HTTP Authentication 1

Masui Masanori - Sep 24 '22 - - Dev Community

Intro

I will try implementing HTTP Authentication with 'Basic' HTTP Authentication.

After authentication, I will set a session key as a cookie value.

Note:
This sample was created simply to see how "Basic" authentication works.
In practice, security measures must be applied.

'Basic' HTTP Authentication

'Basic' HTTP Authentication uses a pair of ID and password for authentication.
And it's based on "Challenge-response authentication".

  1. [Client] Access the protected resources
  2. [Server] Because the client does not have the credentials、The server returns a 401 status code and "WWW-Authenticate".
  3. [Client] Send valid ID and password
  4. [Server] The authentication is succeeded and the server will return the protected resources

2. Returning a 401 status code and "WWW-Authenticate"

At first I will see how web browsers work when the server returns the 401 statu code.

main.go

package main

import (
    "log"
    "net/http"
)

func main() {
    // It only returns 401 status code now. 
    http.HandleFunc("/auth/", func(w http.ResponseWriter, r *http.Request) {
        // TODO: Validate the user authentication info

        // At least one "WWW-Authenticate" is required.
        // Otherwise, web browsers just show the default 401 error page.
        w.Header().Set("WWW-Authenticate", "Basic realm=\"localhost\"")
        w.WriteHeader(401)
    })
    log.Fatal(http.ListenAndServe("localhost:8083", nil))
}
Enter fullscreen mode Exit fullscreen mode

Result

Image description

4. Get the client user-pass(ID and password)

After the client input its ID and password, the server can get them from HTTP request header.

The web brower sets them into HTTP request header as "user-pass".
The "user-pass" is generated in the following format and encoded in Base64.

{ID}:{Password}
Enter fullscreen mode Exit fullscreen mode

The web browser registers it with the "Authorization" key along with the Scheme.

Basic {user-pass}
Enter fullscreen mode Exit fullscreen mode

The server can split them into the Scheme(Basic), the ID, and the password.

main.go

...
import (
    "encoding/base64"
    "log"
    "net/http"
    "strings"
)
...
func signin(r *http.Request) {
    // Get user-pass
    authText := r.Header.Get("Authorization")
    splitted := strings.Split(authText, " ")
    if len(splitted) >= 2 {
        log.Printf("Authorization: %s UserPass: %s", splitted[0], splitted[1])
        decoded, err := base64.StdEncoding.DecodeString(splitted[1])
        if err == nil {
            userPass := string(decoded)
            splitTarget := strings.Index(userPass, ":")
            if splitTarget < 0 {
                log.Println("Invalid")
                return
            }
            userId := userPass[0:splitTarget]
            pass := userPass[splitTarget+1:]
            log.Printf("USER ID:%s Password:%s", userId, pass)
        } else {
            log.Println(err.Error())
        }
    } else {
        log.Println("Invalid")
    }    
}
Enter fullscreen mode Exit fullscreen mode

Results

Authorization: Basic UserPass: ZmZmZ2hqazpkZmdoamts
USER ID:fffghjk Password:dfghjkl
Enter fullscreen mode Exit fullscreen mode

Saving the authentication info

After authentication, how should I keep my credentials?

For example, ASP.NET Core Identity registers authentication information in cookies by default.

I will try registering in cookies in this time.

According to my ASP.NET Core application run, the cookie set after authentication was as follows.

Cookie: .AspNetCore.Identity.Application=CfDJ8PAT8IN6xqxGqh9UWuW_Iif6kvwPRUoA5MEVumnp6qYnZySdcFGwZhWqaznPiKtjbsiyfYLzPmyKHs_wSDYZqSp61aiv-wzRYg5zuisGsOyVpSbKrCUkxUMSTQIrXFdNeVMdb7QDQYJahbbdgjSmZc5kOAuqK8WQh1fCQ0GxbE4XrvwEDClLm-SSRyOC72Ne76uBnGRHPzuGOFXUMklWeRcRWLPEd3ZOwMkwChKkSMA7xJjHAPtCnx0LLkZJ30YmfsbRPJ6qBWiSCoq-ChVzWZLKog0mR4SLBz7K_dEbUfdfMeAi52sXiJkk8rHSaHxxDymF4KJu8-Qg6xZ4e--02xCc-dFedut7Q2NXQPXD7PXY_wV-VB0x60KRQYNc_UeZDvuZWPZi-vgICqgraMFHoynKLEQgRbmTYU6SVRb_MX920fccmtySo8y5-xZ6P_Drmko1hqNyyZKamMKxqHxXXXX
Enter fullscreen mode Exit fullscreen mode

Because I couldn't find a standard specification for storing authentication information in cookies, I figured I had to decide for myself what the key and value of the cookie would be.

Set cookies

expiration := time.Now()
expiration = expiration.AddDate(0, 0, 1)
cookie := http.Cookie{Name: "WebAuthSample", Value: uuid.New().String(), Expires: expiration}
http.SetCookie(w, &cookie)
Enter fullscreen mode Exit fullscreen mode

Get cookies

func getAuthenticationInfo(r *http.Request) string {
    for _, c := range r.Cookies() {
        if c.Name != "WebAuthSample" {
            continue
        }
        // If the client has a valid cookie, it can retrieve the value.
        return c.String()
    }
    return ""
}
Enter fullscreen mode Exit fullscreen mode

TODO

The server-side application should maintain the ID and cookie values of clients who have completed the authentication process, and check the authentication status when the client accesses protected resources.

Resources

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player