DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 966,904 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Masui Masanori
Masui Masanori

Posted on

[Go] Try HTTP Authentication 1

#go

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

Top comments (0)

🌚 Friends don't let friends browse without dark mode.

Sorry, it's true.