IndieAuth #25
|
@ -3,7 +3,6 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"h4kor/owl-blogs"
|
"h4kor/owl-blogs"
|
||||||
"math/rand"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
@ -35,14 +34,7 @@ var resetPasswordCmd = &cobra.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate a random password and print it
|
// generate a random password and print it
|
||||||
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
password := owl.GenerateRandomString(16)
|
||||||
|
|
||||||
b := make([]byte, 16)
|
|
||||||
for i := range b {
|
|
||||||
b[i] = chars[rand.Intn(len(chars))]
|
|
||||||
}
|
|
||||||
password := string(b)
|
|
||||||
|
|
||||||
user.ResetPassword(password)
|
user.ResetPassword(password)
|
||||||
|
|
||||||
fmt.Println("User: ", user.Name())
|
fmt.Println("User: ", user.Name())
|
||||||
|
|
|
@ -16,6 +16,8 @@ func TestAuthPostWrongPassword(t *testing.T) {
|
||||||
repo, user := getSingleUserTestRepo()
|
repo, user := getSingleUserTestRepo()
|
||||||
user.ResetPassword("testpassword")
|
user.ResetPassword("testpassword")
|
||||||
|
|
||||||
|
csrfToken := "test_csrf_token"
|
||||||
|
|
||||||
// Create Request and Response
|
// Create Request and Response
|
||||||
form := url.Values{}
|
form := url.Values{}
|
||||||
form.Add("password", "wrongpassword")
|
form.Add("password", "wrongpassword")
|
||||||
|
@ -23,9 +25,12 @@ func TestAuthPostWrongPassword(t *testing.T) {
|
||||||
form.Add("redirect_uri", "http://example.com/response")
|
form.Add("redirect_uri", "http://example.com/response")
|
||||||
form.Add("response_type", "code")
|
form.Add("response_type", "code")
|
||||||
form.Add("state", "test_state")
|
form.Add("state", "test_state")
|
||||||
|
form.Add("csrf_token", csrfToken)
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", user.AuthUrl()+"verify/", strings.NewReader(form.Encode()))
|
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-Type", "application/x-www-form-urlencoded")
|
||||||
req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
|
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")
|
assertions.AssertNoError(t, err, "Error creating request")
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
router := main.SingleUserRouter(&repo)
|
router := main.SingleUserRouter(&repo)
|
||||||
|
@ -39,6 +44,8 @@ func TestAuthPostCorrectPassword(t *testing.T) {
|
||||||
repo, user := getSingleUserTestRepo()
|
repo, user := getSingleUserTestRepo()
|
||||||
user.ResetPassword("testpassword")
|
user.ResetPassword("testpassword")
|
||||||
|
|
||||||
|
csrfToken := "test_csrf_token"
|
||||||
|
|
||||||
// Create Request and Response
|
// Create Request and Response
|
||||||
form := url.Values{}
|
form := url.Values{}
|
||||||
form.Add("password", "testpassword")
|
form.Add("password", "testpassword")
|
||||||
|
@ -46,9 +53,11 @@ func TestAuthPostCorrectPassword(t *testing.T) {
|
||||||
form.Add("redirect_uri", "http://example.com/response")
|
form.Add("redirect_uri", "http://example.com/response")
|
||||||
form.Add("response_type", "code")
|
form.Add("response_type", "code")
|
||||||
form.Add("state", "test_state")
|
form.Add("state", "test_state")
|
||||||
|
form.Add("csrf_token", csrfToken)
|
||||||
req, err := http.NewRequest("POST", user.AuthUrl()+"verify/", strings.NewReader(form.Encode()))
|
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-Type", "application/x-www-form-urlencoded")
|
||||||
req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
|
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")
|
assertions.AssertNoError(t, err, "Error creating request")
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
router := main.SingleUserRouter(&repo)
|
router := main.SingleUserRouter(&repo)
|
||||||
|
|
|
@ -100,6 +100,15 @@ func userAuthHandler(repo *owl.Repository) func(http.ResponseWriter, *http.Reque
|
||||||
return
|
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{
|
reqData := owl.AuthRequestData{
|
||||||
Me: me,
|
Me: me,
|
||||||
ClientId: clientId,
|
ClientId: clientId,
|
||||||
|
@ -107,6 +116,7 @@ func userAuthHandler(repo *owl.Repository) func(http.ResponseWriter, *http.Reque
|
||||||
State: state,
|
State: state,
|
||||||
ResponseType: responseType,
|
ResponseType: responseType,
|
||||||
User: user,
|
User: user,
|
||||||
|
CsrfToken: csrfToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
html, err := owl.RenderUserAuthPage(reqData)
|
html, err := owl.RenderUserAuthPage(reqData)
|
||||||
|
@ -204,6 +214,23 @@ func userAuthVerifyHandler(repo *owl.Repository) func(http.ResponseWriter, *http
|
||||||
response_type := r.FormValue("response_type")
|
response_type := r.FormValue("response_type")
|
||||||
state := r.FormValue("state")
|
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)
|
password_valid := user.VerifyPassword(password)
|
||||||
if !password_valid {
|
if !password_valid {
|
||||||
http.Redirect(w, r,
|
http.Redirect(w, r,
|
||||||
|
|
|
@ -7,5 +7,6 @@
|
||||||
<input type="hidden" name="redirect_uri" value="{{.RedirectUri}}">
|
<input type="hidden" name="redirect_uri" value="{{.RedirectUri}}">
|
||||||
<input type="hidden" name="response_type" value="{{.ResponseType}}">
|
<input type="hidden" name="response_type" value="{{.ResponseType}}">
|
||||||
<input type="hidden" name="state" value="{{.State}}">
|
<input type="hidden" name="state" value="{{.State}}">
|
||||||
|
<input type="hidden" name="csrf_token" value="{{.CsrfToken}}">
|
||||||
<input type="submit" value="Login">
|
<input type="submit" value="Login">
|
||||||
</form>
|
</form>
|
|
@ -27,6 +27,7 @@ type AuthRequestData struct {
|
||||||
State string
|
State string
|
||||||
ResponseType string
|
ResponseType string
|
||||||
User User
|
User User
|
||||||
|
CsrfToken string
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderEmbedTemplate(templateFile string, data interface{}) (string, error) {
|
func renderEmbedTemplate(templateFile string, data interface{}) (string, error) {
|
||||||
|
|
8
user.go
8
user.go
|
@ -2,7 +2,6 @@ package owl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
@ -287,12 +286,7 @@ func (user User) addAuthCode(code AuthCode) error {
|
||||||
|
|
||||||
func (user User) GenerateAuthCode(client_id string, redirect_uri string) (string, error) {
|
func (user User) GenerateAuthCode(client_id string, redirect_uri string) (string, error) {
|
||||||
// generate code
|
// generate code
|
||||||
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
code := GenerateRandomString(32)
|
||||||
b := make([]byte, 32)
|
|
||||||
for i := range b {
|
|
||||||
b[i] = chars[rand.Intn(len(chars))]
|
|
||||||
}
|
|
||||||
code := string(b)
|
|
||||||
return code, user.addAuthCode(AuthCode{
|
return code, user.addAuthCode(AuthCode{
|
||||||
Code: code,
|
Code: code,
|
||||||
ClientId: client_id,
|
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