auth/auth.go

194 lines
4.7 KiB
Go
Raw Normal View History

2024-09-26 09:06:17 +00:00
package auth
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
2024-09-13 09:05:36 +00:00
"time"
)
2024-09-15 18:42:07 +00:00
type User struct {
Sub string `json:"sub"`
}
type AuthentikUser struct {
User
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
Name string `json:"name"`
GivenName string `json:"given_name"`
PreferredUsername string `json:"preferred_username"`
Nickname string `json:"nickname"`
Groups []string `json:"groups"`
}
type Auth struct {
2024-09-13 09:05:36 +00:00
authConfig AuthConfig
clientConfig ClientConfig
}
2024-09-13 09:05:36 +00:00
type Token struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiredIn int `json:"expires_in"`
RefreshToken string `json:"refresh_token"`
CreatedAt time.Time
}
func NewAuthWithConfig(config ClientConfig, authConfig AuthConfig) (Auth, error) {
a := Auth{}
2024-09-13 09:05:36 +00:00
a.authConfig = authConfig
a.clientConfig = config
return a, nil
}
2024-09-13 09:05:36 +00:00
func NewAuthWithConfigurationURL(config ClientConfig, url string) (Auth, error) {
a := Auth{}
2024-09-13 09:05:36 +00:00
a.clientConfig = config
authConfig := AuthConfig{}
res, err := http.Get(url)
if err != nil {
return Auth{}, fmt.Errorf("%w: %q", ErrCantGetConfiguratorData, err)
}
defer res.Body.Close()
bodyContent, err := io.ReadAll(res.Body)
if err != nil {
return Auth{}, fmt.Errorf("%w: %q", ErrCantGetConfiguratorData, err)
}
2024-09-13 09:05:36 +00:00
err = json.Unmarshal(bodyContent, &authConfig)
if err != nil {
return Auth{}, fmt.Errorf("%w: %q", ErrCantGetConfiguratorData, err)
}
2024-09-13 09:05:36 +00:00
a.authConfig = authConfig
return a, nil
}
2024-09-13 09:05:36 +00:00
func (a Auth) GetAuthorizationURL(state string) (string, error) {
if a.authConfig.AuthorizationEndpoint == "" {
return "", fmt.Errorf("%w: %s", ErrCantGetAuthorizationURL, "AuthorizationEndpoint in config is empty")
}
2024-09-13 09:05:36 +00:00
if a.clientConfig.ClientID == "" {
return "", fmt.Errorf("%w: %s", ErrCantGetAuthorizationURL, "clientid in config is empty")
}
2024-09-13 09:05:36 +00:00
url, err := url.Parse(a.authConfig.AuthorizationEndpoint)
if err != nil {
return "", fmt.Errorf("%w: %q", ErrCantGetAuthorizationURL, err)
}
values := url.Query()
2024-09-13 09:05:36 +00:00
values.Set("client_id", a.clientConfig.ClientID)
if a.clientConfig.RedirectURL != "" {
values.Set("redirect_uri", a.clientConfig.RedirectURL)
}
2024-09-13 09:05:36 +00:00
if len(a.clientConfig.Scope) > 0 {
values.Set("scope", strings.Join(a.clientConfig.Scope, "+"))
}
if state != "" {
values.Set("state", state)
}
values.Set("response_type", "code")
url.RawQuery = values.Encode()
return url.String(), nil
}
2024-09-13 09:05:36 +00:00
2024-09-15 18:42:07 +00:00
func (a Auth) GetUserInfo(accessToken string, user any) error {
req, err := http.NewRequest("GET", a.authConfig.UserinfoEndpoint, nil)
req.Header.Add("Authorization", "Bearer "+accessToken)
hc := http.Client{}
resp, err := hc.Do(req)
if err != nil {
return fmt.Errorf("%w: %q", ErrCreateRequestForUserInfo, err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("%w: %s (%v)", ErrCantGetUserInfo, "server response with nuon 200 status code", resp.StatusCode)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("%w: %q", ErrCreateRequestForUserInfo, err)
}
err = json.Unmarshal(body, &user)
if err != nil {
return fmt.Errorf("%w: %s: %q", ErrCantGetUserInfo, "json unmarshal", err)
}
return nil
}
2024-09-13 09:05:36 +00:00
func (a Auth) GetTokenFromCode(code string) (Token, error) {
form := url.Values{}
form.Add("grant_type", "authorization_code")
form.Add("code", code)
2024-09-13 21:41:29 +00:00
req, err := http.NewRequest("POST", a.authConfig.TokenEndpoint, strings.NewReader(form.Encode()))
if err != nil {
return Token{}, fmt.Errorf("%w: %q", ErrCantCreateTokenRequests, err)
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.SetBasicAuth(a.clientConfig.ClientID, a.clientConfig.ClientSecret)
hc := http.Client{}
resp, err := hc.Do(req)
if err != nil {
return Token{}, fmt.Errorf("%w: %q", ErrCantSendRequestsForToken, err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return Token{}, fmt.Errorf("%w: %q", ErrCantSendRequestsForToken, err)
}
fmt.Println(string(body))
fmt.Println(resp.StatusCode)
if resp.StatusCode != 200 {
var er struct {
Error string `json:"error"`
ErrorDescription string `json:"error_description"`
}
err = json.Unmarshal(body, &er)
if err != nil {
return Token{}, fmt.Errorf("%w: %s", ErrWrongResponseFromServer, string(body))
}
if er.ErrorDescription != "" {
return Token{}, fmt.Errorf("%w: %s", ErrWrongResponseFromServer, er.ErrorDescription)
}
return Token{}, fmt.Errorf("%w: %s", ErrWrongResponseFromServer, string(body))
}
t := Token{}
t.CreatedAt = time.Now()
err = json.Unmarshal(body, &t)
if err != nil {
return Token{}, fmt.Errorf("%w: %q", ErrCantGetTokenForCode, err)
}
2024-09-13 09:05:36 +00:00
return t, nil
}