Skip to content

Commit

Permalink
auth pkg
Browse files Browse the repository at this point in the history
  • Loading branch information
rucsi committed Apr 16, 2024
1 parent ce15aa9 commit e633eee
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 95 deletions.
101 changes: 6 additions & 95 deletions internal/api/auth.go
Original file line number Diff line number Diff line change
@@ -1,46 +1,24 @@
package api

import (
"encoding/base64"
"fmt"
"net/http"
"net/url"
"path"
"strings"
"time"

"github.com/go-chi/chi/v5"
"github.com/rs/xid"

"github.com/moonwalker/moonbase/internal/env"
"github.com/moonwalker/moonbase/internal/jwt"
gh "github.com/moonwalker/moonbase/pkg/github"
)

// test the flow:
// http://localhost:8080/login/github?return_url=/login/github/authenticate

const (
retUrlCodePath = 0
retUrlCodeQuery = 1
oauthStateSep = "|"
codeTokenExpires = time.Minute
accessTokenExpires = time.Hour * 24
)

var (
oauthStateSecret = xid.New().String()
)

type User struct {
Login *string `json:"login"`
Email *string `json:"email"`
Image *string `json:"image"`
Token string `json:"token"`
}

func githubAuth(w http.ResponseWriter, r *http.Request) {
state, err := encodeState(r)
state, err := gh.EncodeState(r, oauthStateSecret)
if err != nil {
errAuthEncState().Log(r, err).Json(w)
return
Expand All @@ -51,15 +29,15 @@ func githubAuth(w http.ResponseWriter, r *http.Request) {
}

func githubCallback(w http.ResponseWriter, r *http.Request) {
secret, returnURL := decodeState(r)
secret, returnURL := gh.DecodeState(r)
if secret != oauthStateSecret {
err := fmt.Errorf("expected: %s actual: %s", oauthStateSecret, secret)
errAuthBadSecret().Log(r, err).Json(w)
return
}

code := r.FormValue("code")
url, err := returnURLWithCode(returnURL, code, retUrlCodePath)
url, err := gh.ReturnURLWithCode(returnURL, code, gh.RetUrlCodePath)
if err != nil {
errAuthEncRetURL().Log(r, err).Json(w)
return
Expand All @@ -79,7 +57,7 @@ func authenticateHandler(w http.ResponseWriter, r *http.Request) {
}
}

decoded, err := decryptExchangeCode(code)
decoded, err := gh.DecryptExchangeCode(code)
if err != nil {
errAuthDecOAuth().Log(r, err).Json(w)
return
Expand All @@ -97,13 +75,13 @@ func authenticateHandler(w http.ResponseWriter, r *http.Request) {
return
}

et, err := encryptAccessToken(token)
et, err := gh.EncryptAccessToken(token)
if err != nil {
errAuthEncToken().Log(r, err).Json(w)
return
}

usr := &User{
usr := &gh.User{
Login: ghUser.Login,
Email: ghUser.Email,
Image: ghUser.AvatarURL,
Expand All @@ -117,70 +95,3 @@ func authenticateHandler(w http.ResponseWriter, r *http.Request) {

jsonResponse(w, http.StatusOK, usr)
}

func encryptAccessToken(accessToken string) (string, error) {
te, err := jwt.EncryptAndSign(env.JweKey, env.JwtKey, []byte(accessToken), accessTokenExpires)
if err != nil {
return "", err
}

return te, nil
}

func encodeState(r *http.Request) (string, error) {
returnURL := r.FormValue("return_url")

u, err := url.Parse(returnURL)
if err != nil {
return "", err
}

if !u.IsAbs() {
u, err = url.Parse(r.Referer())
if err != nil {
return "", err
}
u.Path = returnURL
}

state := fmt.Sprintf("%s%s%s", oauthStateSecret, oauthStateSep, u)
return base64.URLEncoding.EncodeToString([]byte(state)), nil
}

func decodeState(r *http.Request) (string, string) {
state, _ := base64.URLEncoding.DecodeString(r.FormValue("state"))
parts := strings.Split(string(state), oauthStateSep)
return parts[0], parts[1]
}

func returnURLWithCode(returnURL, code string, m int) (string, error) {
u, err := url.Parse(returnURL)
if err != nil {
return "", err
}

codeToken, err := jwt.EncryptAndSign(env.JweKey, env.JwtKey, []byte(code), codeTokenExpires)
if err != nil {
return "", err
}

switch {
case m == retUrlCodeQuery:
u.RawQuery = url.Values{
"code": {codeToken},
}.Encode()
case m == retUrlCodePath:
u.Path = path.Join(u.Path, codeToken)
}

return u.String(), nil
}

func decryptExchangeCode(code string) (string, error) {
token, err := jwt.VerifyAndDecrypt(env.JweKey, env.JwtKey, code)
if err != nil {
return "", err
}

return string(token.Claims.(*jwt.AuthClaims).Data), nil
}
85 changes: 85 additions & 0 deletions pkg/github/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ package github

import (
"context"
"encoding/base64"
"fmt"
"net/http"
"net/url"
"path"
"strings"
"time"

"github.com/moonwalker/moonbase/internal/env"
"github.com/moonwalker/moonbase/internal/jwt"
Expand All @@ -14,8 +19,21 @@ type ctxKey int
const (
ctxKeyAccessToken ctxKey = iota
ctxKeyUser ctxKey = iota

RetUrlCodePath = 0
RetUrlCodeQuery = 1
oauthStateSep = "|"
codeTokenExpires = time.Minute
accessTokenExpires = time.Hour * 24
)

type User struct {
Login *string `json:"login"`
Email *string `json:"email"`
Image *string `json:"image"`
Token string `json:"token"`
}

func WithUser(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var tokenString string
Expand Down Expand Up @@ -62,3 +80,70 @@ func AccessTokenFromContext(ctx context.Context) string {
func UserFromContext(ctx context.Context) string {
return ctx.Value(ctxKeyUser).(string)
}

func EncryptAccessToken(accessToken string) (string, error) {
te, err := jwt.EncryptAndSign(env.JweKey, env.JwtKey, []byte(accessToken), accessTokenExpires)
if err != nil {
return "", err
}

return te, nil
}

func EncodeState(r *http.Request, oauthStateSecret string) (string, error) {
returnURL := r.FormValue("return_url")

u, err := url.Parse(returnURL)
if err != nil {
return "", err
}

if !u.IsAbs() {
u, err = url.Parse(r.Referer())
if err != nil {
return "", err
}
u.Path = returnURL
}

state := fmt.Sprintf("%s%s%s", oauthStateSecret, oauthStateSep, u)
return base64.URLEncoding.EncodeToString([]byte(state)), nil
}

func DecodeState(r *http.Request) (string, string) {
state, _ := base64.URLEncoding.DecodeString(r.FormValue("state"))
parts := strings.Split(string(state), oauthStateSep)
return parts[0], parts[1]
}

func ReturnURLWithCode(returnURL, code string, m int) (string, error) {
u, err := url.Parse(returnURL)
if err != nil {
return "", err
}

codeToken, err := jwt.EncryptAndSign(env.JweKey, env.JwtKey, []byte(code), codeTokenExpires)
if err != nil {
return "", err
}

switch {
case m == RetUrlCodeQuery:
u.RawQuery = url.Values{
"code": {codeToken},
}.Encode()
case m == RetUrlCodePath:
u.Path = path.Join(u.Path, codeToken)
}

return u.String(), nil
}

func DecryptExchangeCode(code string) (string, error) {
token, err := jwt.VerifyAndDecrypt(env.JweKey, env.JwtKey, code)
if err != nil {
return "", err
}

return string(token.Claims.(*jwt.AuthClaims).Data), nil
}

0 comments on commit e633eee

Please sign in to comment.