IndieAuth #25
|
@ -3,7 +3,6 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
"h4kor/owl-blogs"
|
||||
"math/rand"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
@ -35,14 +34,7 @@ var resetPasswordCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
// generate a random password and print it
|
||||
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
b := make([]byte, 16)
|
||||
for i := range b {
|
||||
b[i] = chars[rand.Intn(len(chars))]
|
||||
}
|
||||
password := string(b)
|
||||
|
||||
password := owl.GenerateRandomString(16)
|
||||
user.ResetPassword(password)
|
||||
|
||||
fmt.Println("User: ", user.Name())
|
||||
|
|
|
@ -16,6 +16,8 @@ func TestAuthPostWrongPassword(t *testing.T) {
|
|||
repo, user := getSingleUserTestRepo()
|
||||
user.ResetPassword("testpassword")
|
||||
|
||||
csrfToken := "test_csrf_token"
|
||||
|
||||
// Create Request and Response
|
||||
form := url.Values{}
|
||||
form.Add("password", "wrongpassword")
|
||||
|
@ -23,9 +25,12 @@ func TestAuthPostWrongPassword(t *testing.T) {
|
|||
form.Add("redirect_uri", "http://example.com/response")
|
||||
form.Add("response_type", "code")
|
||||
form.Add("state", "test_state")
|
||||
form.Add("csrf_token", csrfToken)
|
||||
|
||||
req, err := http.NewRequest("POST", user.AuthUrl()+"verify/", strings.NewReader(form.Encode()))
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
|
||||
req.AddCookie(&http.Cookie{Name: "csrf_token", Value: csrfToken})
|
||||
assertions.AssertNoError(t, err, "Error creating request")
|
||||
rr := httptest.NewRecorder()
|
||||
router := main.SingleUserRouter(&repo)
|
||||
|
@ -39,6 +44,8 @@ func TestAuthPostCorrectPassword(t *testing.T) {
|
|||
repo, user := getSingleUserTestRepo()
|
||||
user.ResetPassword("testpassword")
|
||||
|
||||
csrfToken := "test_csrf_token"
|
||||
|
||||
// Create Request and Response
|
||||
form := url.Values{}
|
||||
form.Add("password", "testpassword")
|
||||
|
@ -46,9 +53,11 @@ func TestAuthPostCorrectPassword(t *testing.T) {
|
|||
form.Add("redirect_uri", "http://example.com/response")
|
||||
form.Add("response_type", "code")
|
||||
form.Add("state", "test_state")
|
||||
form.Add("csrf_token", csrfToken)
|
||||
req, err := http.NewRequest("POST", user.AuthUrl()+"verify/", strings.NewReader(form.Encode()))
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
|
||||
req.AddCookie(&http.Cookie{Name: "csrf_token", Value: csrfToken})
|
||||
assertions.AssertNoError(t, err, "Error creating request")
|
||||
rr := httptest.NewRecorder()
|
||||
router := main.SingleUserRouter(&repo)
|
||||
|
|
|
@ -100,6 +100,15 @@ func userAuthHandler(repo *owl.Repository) func(http.ResponseWriter, *http.Reque
|
|||
return
|
||||
}
|
||||
|
||||
// Double Submit Cookie Pattern
|
||||
// https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie
|
||||
csrfToken := owl.GenerateRandomString(32)
|
||||
cookie := http.Cookie{
|
||||
Name: "csrf_token",
|
||||
Value: csrfToken,
|
||||
}
|
||||
http.SetCookie(w, &cookie)
|
||||
|
||||
reqData := owl.AuthRequestData{
|
||||
Me: me,
|
||||
ClientId: clientId,
|
||||
|
@ -107,6 +116,7 @@ func userAuthHandler(repo *owl.Repository) func(http.ResponseWriter, *http.Reque
|
|||
State: state,
|
||||
ResponseType: responseType,
|
||||
User: user,
|
||||
CsrfToken: csrfToken,
|
||||
}
|
||||
|
||||
html, err := owl.RenderUserAuthPage(reqData)
|
||||
|
@ -204,6 +214,23 @@ func userAuthVerifyHandler(repo *owl.Repository) func(http.ResponseWriter, *http
|
|||
response_type := r.FormValue("response_type")
|
||||
state := r.FormValue("state")
|
||||
|
||||
// CSRF check
|
||||
formCsrfToken := r.FormValue("csrf_token")
|
||||
cookieCsrfToken, err := r.Cookie("csrf_token")
|
||||
|
||||
if err != nil {
|
||||
println("Error getting csrf token from cookie: ", err.Error())
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte("Error getting csrf token from cookie"))
|
||||
return
|
||||
}
|
||||
if formCsrfToken != cookieCsrfToken.Value {
|
||||
println("Invalid csrf token")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte("Invalid csrf token"))
|
||||
return
|
||||
}
|
||||
|
||||
password_valid := user.VerifyPassword(password)
|
||||
if !password_valid {
|
||||
http.Redirect(w, r,
|
||||
|
|
|
@ -7,5 +7,6 @@
|
|||
<input type="hidden" name="redirect_uri" value="{{.RedirectUri}}">
|
||||
<input type="hidden" name="response_type" value="{{.ResponseType}}">
|
||||
<input type="hidden" name="state" value="{{.State}}">
|
||||
<input type="hidden" name="csrf_token" value="{{.CsrfToken}}">
|
||||
<input type="submit" value="Login">
|
||||
</form>
|
|
@ -27,6 +27,7 @@ type AuthRequestData struct {
|
|||
State string
|
||||
ResponseType string
|
||||
User User
|
||||
CsrfToken string
|
||||
}
|
||||
|
||||
func renderEmbedTemplate(templateFile string, data interface{}) (string, error) {
|
||||
|
|
8
user.go
8
user.go
|
@ -2,7 +2,6 @@ package owl
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
|
@ -287,12 +286,7 @@ func (user User) addAuthCode(code AuthCode) error {
|
|||
|
||||
func (user User) GenerateAuthCode(client_id string, redirect_uri string) (string, error) {
|
||||
// generate code
|
||||
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
b := make([]byte, 32)
|
||||
for i := range b {
|
||||
b[i] = chars[rand.Intn(len(chars))]
|
||||
}
|
||||
code := string(b)
|
||||
code := GenerateRandomString(32)
|
||||
return code, user.addAuthCode(AuthCode{
|
||||
Code: code,
|
||||
ClientId: client_id,
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package owl
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
func GenerateRandomString(length int) string {
|
||||
chars := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
||||
b := make([]rune, length)
|
||||
for i := range b {
|
||||
k, _ := rand.Int(rand.Reader, big.NewInt(int64(len(chars))))
|
||||
b[i] = chars[k.Int64()]
|
||||
}
|
||||
return string(b)
|
||||
}
|
Loading…
Reference in New Issue