Closed ezielramos closed 2 years ago
func GenerateSecureToken(length int) string {
b := make([]byte, length)
if _, err := rand.Read(b); err != nil {
return ""
}
return hex.EncodeToString(b)
}
package main
import ( "crypto/md5" "encoding/hex" "fmt" "log"
"golang.org/x/crypto/bcrypt"
)
// GenerateToken returns a unique token based on the provided email string func GenerateToken(email string) string { hash, err := bcrypt.GenerateFromPassword([]byte(email), bcrypt.DefaultCost) if err != nil { log.Fatal(err) } fmt.Println("Hash to store:", string(hash))
hasher := md5.New()
hasher.Write(hash)
return hex.EncodeToString(hasher.Sum(nil))
}
func main() { fmt.Println("token:", GenerateToken("bob@webserver.com")) }
- this code generate a short token
- output:
$ go run main.go Hash to store: $2a$10$B23cv7lDpbY3iVvfZ7GYE.e4691ow8i7l6CQXkmz315fbg4jLzoue token: 90a514ab93e2c32fdd1072154b26a100
### Option 3: base64
package main
import ( "encoding/base64" "fmt" "log"
"golang.org/x/crypto/bcrypt"
)
// GenerateToken returns a unique token based on the provided email string func GenerateToken(email string) string { hash, err := bcrypt.GenerateFromPassword([]byte(email), bcrypt.DefaultCost) if err != nil { log.Fatal(err) } fmt.Println("Hash to store:", string(hash))
return base64.StdEncoding.EncodeToString(hash)
}
func main() { fmt.Println("token:", GenerateToken("bob@webserver.com")) }
- output:
$ go run main.go Hash to store: $2a$10$cbVMU9U665VSqpfwrNZWOeU5cIDOe5iBJ8ZVa2yJCTsnk9MEZHvRq token: JDJhJDEwJGNiVk1VOVU2NjVWU3FwZndyTlpXT2VVNWNJRE9lNWlCSjhaVmEyeUpDVHNuazlNRVpIdlJx
- option 2 and 3 are from: https://stackoverflow.com/a/45267661
### Option 4 IBM
/go-sdk-core
import "github.com/IBM/go-sdk-core/v5/core" ... // Create the authenticator. authenticator, err := core.NewBasicAuthenticator("myuser", "mypassword") if err != nil { panic(err) }
- description link: https://github.com/IBM/go-sdk-core/blob/main/Authentication.md
### Option 5 Implement Token-based Authentication with Golang and MySQL 8 Server:
- Token-based authentication is a form of access control protocol. To implement it, you require users to verify their identity (For instance, by providing their usernames and passwords). Then, you issue the users with a time-based secret that they can use to access your application.
- Prerequisites: A Linux server, A non-root sudo user, A MySQL database,The Golang package
- Follow this link for the description and complete implementation: https://www.vultr.com/docs/implement-tokenbased-authentication-with-golang-and-mysql-8-server/#:~:text=Introduction,use%20to%20access%20your%20application.
- This is how look authentications.go
package main
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
"golang.org/x/crypto/bcrypt"
"time"
"crypto/rand"
"encoding/base64"
"errors"
)
func generateToken(username string, password string) (map[string]interface{}, error) {
db, err := sql.Open("mysql", "sample_db_user:EXAMPLE_PASSWORD@tcp(127.0.0.1:3306)/sample_db")
if err != nil {
return nil, err
}
queryString := "select user_id, password from system_users where username = ?"
stmt, err := db.Prepare(queryString)
if err != nil {
return nil, err
}
defer stmt.Close()
userId := 0
accountPassword := ""
err = stmt.QueryRow(username).Scan(&userId, &accountPassword)
if err != nil {
if err == sql.ErrNoRows {
return nil, errors.New("Invalid username or password.\r\n")
}
return nil, err
}
err = bcrypt.CompareHashAndPassword([]byte(accountPassword), []byte(password))
if err != nil {
return nil, errors.New("Invalid username or password.\r\n")
}
queryString = "insert into authentication_tokens(user_id, auth_token, generated_at, expires_at) values (?, ?, ?, ?)"
stmt, err = db.Prepare(queryString)
if err != nil {
return nil, err
}
defer stmt.Close()
randomToken := make([]byte, 32)
_, err = rand.Read(randomToken)
if err != nil {
return nil, err
}
authToken := base64.URLEncoding.EncodeToString(randomToken)
const timeLayout = "2006-01-02 15:04:05"
dt := time.Now()
expirtyTime := time.Now().Add(time.Minute * 60)
generatedAt := dt.Format(timeLayout)
expiresAt := expirtyTime.Format(timeLayout)
_, err = stmt.Exec(userId, authToken, generatedAt, expiresAt)
if err != nil {
return nil, err
}
tokenDetails := map[string]interface{}{
"token_type": "Bearer",
"auth_token" : authToken,
"generated_at": generatedAt,
"expires_at": expiresAt,
}
return tokenDetails, nil
}
func validateToken(authToken string) (map[string]interface{}, error) {
db, err := sql.Open("mysql", "sample_db_user:EXAMPLE_PASSWORD@tcp(127.0.0.1:3306)/sample_db")
if err != nil {
return nil, err
}
queryString := `select
system_users.user_id,
username,
generated_at,
expires_at
from authentication_tokens
left join system_users
on authentication_tokens.user_id = system_users.user_id
where auth_token = ?`
stmt, err := db.Prepare(queryString)
if err != nil {
return nil, err
}
defer stmt.Close()
userId := 0
username := ""
generatedAt := ""
expiresAt := ""
err = stmt.QueryRow(authToken).Scan(&userId, &username, &generatedAt, &expiresAt)
if err != nil {
if err == sql.ErrNoRows {
return nil, errors.New("Invalid access token.\r\n")
}
return nil, err
}
const timeLayout = "2006-01-02 15:04:05"
expiryTime, _ := time.Parse(timeLayout, expiresAt)
currentTime, _ := time.Parse(timeLayout, time.Now().Format(timeLayout))
if expiryTime.Before(currentTime) {
return nil, errors.New("The token is expired.\r\n")
}
userDetails := map[string]interface{}{
"user_id": userId,
"username": username,
"generated_at": generatedAt,
"expires_at" : expiresAt,
}
return userDetails, nil
}
## Protocol
- https://docs.wso2.com/display/AM190/Generating+Access+Tokens+with+User+Credentials+-+Password+Grant+Type
How to solve authentication problem ?
Story points: 3