feat: create service session

This commit is contained in:
2025-06-15 21:02:38 +02:00
parent b36b6e18ca
commit 41d439beab

View File

@ -17,20 +17,10 @@ import (
"github.com/google/uuid"
)
type ApiToken struct {
Token string
Expiration float64
}
type ApiTokens struct {
ID ApiToken
Access ApiToken
Refresh ApiToken
}
func (h *OAuthHandler) signApiTokens(user *repository.User, apiService *repository.ApiService, nonce *string) (*ApiTokens, error) {
func (h *OAuthHandler) signApiTokens(user *repository.User, apiService *repository.ApiService, nonce *string) (*types.SignedToken, *types.SignedToken, *types.SignedToken, error) {
accessExpiresIn := 15 * time.Minute
accessExpiresAt := time.Now().Add(accessExpiresIn)
accessJTI := uuid.New()
accessClaims := types.ApiClaims{
Permissions: []string{},
@ -40,12 +30,13 @@ func (h *OAuthHandler) signApiTokens(user *repository.User, apiService *reposito
Audience: jwt.ClaimStrings{apiService.ClientID},
IssuedAt: jwt.NewNumericDate(time.Now()),
ExpiresAt: jwt.NewNumericDate(accessExpiresAt),
ID: accessJTI.String(),
},
}
access, err := util.SignJwtToken(accessClaims, h.cfg.Jwt.PrivateKey)
if err != nil {
return nil, err
return nil, nil, nil, err
}
var roles = []string{"user"}
@ -56,6 +47,7 @@ func (h *OAuthHandler) signApiTokens(user *repository.User, apiService *reposito
idExpiresIn := 15 * time.Minute
idExpiresAt := time.Now().Add(idExpiresIn)
idJTI := uuid.New()
idClaims := types.IdTokenClaims{
Email: user.Email,
@ -70,16 +62,18 @@ func (h *OAuthHandler) signApiTokens(user *repository.User, apiService *reposito
Audience: jwt.ClaimStrings{apiService.ClientID},
IssuedAt: jwt.NewNumericDate(time.Now()),
ExpiresAt: jwt.NewNumericDate(idExpiresAt),
ID: idJTI.String(),
},
}
idToken, err := util.SignJwtToken(idClaims, h.cfg.Jwt.PrivateKey)
if err != nil {
return nil, err
return nil, nil, nil, err
}
refreshExpiresIn := 24 * time.Hour
refreshExpiresAt := time.Now().Add(refreshExpiresIn)
refreshJTI := uuid.New()
refreshClaims := types.ApiRefreshClaims{
UserID: user.ID.String(),
@ -89,28 +83,16 @@ func (h *OAuthHandler) signApiTokens(user *repository.User, apiService *reposito
Audience: jwt.ClaimStrings{apiService.ClientID},
IssuedAt: jwt.NewNumericDate(time.Now()),
ExpiresAt: jwt.NewNumericDate(refreshExpiresAt),
ID: refreshJTI.String(),
},
}
refresh, err := util.SignJwtToken(refreshClaims, h.cfg.Jwt.PrivateKey)
if err != nil {
return nil, err
return nil, nil, nil, err
}
return &ApiTokens{
ID: ApiToken{
Token: idToken,
Expiration: idExpiresIn.Seconds(),
},
Access: ApiToken{
Token: access,
Expiration: accessExpiresIn.Seconds(),
},
Refresh: ApiToken{
Token: refresh,
Expiration: refreshExpiresIn.Seconds(),
},
}, nil
return types.NewSignedToken(idToken, idExpiresAt, idJTI), types.NewSignedToken(access, accessExpiresAt, accessJTI), types.NewSignedToken(refresh, refreshExpiresAt, refreshJTI), nil
}
func (h *OAuthHandler) tokenEndpoint(w http.ResponseWriter, r *http.Request) {
@ -174,40 +156,71 @@ func (h *OAuthHandler) tokenEndpoint(w http.ResponseWriter, r *http.Request) {
fmt.Printf("Code received: %s\n", code)
session, err := h.cache.GetAuthCode(r.Context(), code)
codeSession, err := h.cache.GetAuthCode(r.Context(), code)
if err != nil {
log.Printf("ERR: Failed to find session under the code %s: %v\n", code, err)
web.Error(w, "no session found under this auth code", http.StatusNotFound)
return
}
log.Printf("DEBUG: Fetched code session: %#v\n", session)
log.Printf("DEBUG: Fetched code session: %#v\n", codeSession)
apiService, err := h.repo.GetApiServiceCID(r.Context(), session.ClientID)
apiService, err := h.repo.GetApiServiceCID(r.Context(), codeSession.ClientID)
if err != nil {
log.Printf("ERR: Could not find API service with client %s: %v\n", session.ClientID, err)
log.Printf("ERR: Could not find API service with client %s: %v\n", codeSession.ClientID, err)
web.Error(w, "service is not registered", http.StatusForbidden)
return
}
if session.ClientID != clientId {
if codeSession.ClientID != clientId {
web.Error(w, "invalid auth", http.StatusUnauthorized)
return
}
user, err := h.repo.FindUserId(r.Context(), uuid.MustParse(session.UserID))
user, err := h.repo.FindUserId(r.Context(), uuid.MustParse(codeSession.UserID))
if err != nil {
web.Error(w, "requested user not found", http.StatusNotFound)
return
}
tokens, err := h.signApiTokens(&user, &apiService, &session.Nonce)
id, access, refresh, err := h.signApiTokens(&user, &apiService, &codeSession.Nonce)
if err != nil {
log.Println("ERR: Failed to sign api tokens:", err)
web.Error(w, "failed to sign tokens", http.StatusInternalServerError)
return
}
log.Printf("DEBUG: Created api tokens: %v\n\n%v\n\n%v\n", id.ID.String(), access.ID.String(), refresh.ID.String())
userId, err := uuid.Parse(codeSession.UserID)
if err != nil {
log.Printf("ERR: Failed to parse user '%s' uuid: %v\n", codeSession.UserID, err)
web.Error(w, "failed to sign tokens", http.StatusInternalServerError)
return
}
ipAddr := util.GetClientIP(r)
ua := r.UserAgent()
session, err := h.repo.CreateServiceSession(r.Context(), repository.CreateServiceSessionParams{
ServiceID: apiService.ID,
ClientID: apiService.ClientID,
UserID: &userId,
ExpiresAt: &refresh.ExpiresAt,
LastActive: nil,
IpAddress: &ipAddr,
UserAgent: &ua,
AccessTokenID: &access.ID,
RefreshTokenID: &refresh.ID,
})
if err != nil {
log.Printf("ERR: Failed to create new service session: %v\n", err)
web.Error(w, "failed to create session", http.StatusInternalServerError)
return
}
log.Printf("INFO: Service session created for '%s' client_id with '%s' id\n", apiService.ClientID, session.ID.String())
type Response struct {
IdToken string `json:"id_token"`
TokenType string `json:"token_type"`
@ -219,11 +232,11 @@ func (h *OAuthHandler) tokenEndpoint(w http.ResponseWriter, r *http.Request) {
}
response := Response{
IdToken: tokens.ID.Token,
IdToken: id.Token,
TokenType: "Bearer",
AccessToken: tokens.Access.Token,
RefreshToken: tokens.Refresh.Token,
ExpiresIn: tokens.Access.Expiration,
AccessToken: access.Token,
RefreshToken: refresh.Token,
ExpiresIn: access.ExpiresAt.Sub(time.Now()).Seconds(),
Email: user.Email,
}
@ -269,7 +282,7 @@ func (h *OAuthHandler) tokenEndpoint(w http.ResponseWriter, r *http.Request) {
return
}
tokens, err := h.signApiTokens(&user, &apiService, nil)
id, access, refresh, err := h.signApiTokens(&user, &apiService, nil)
type Response struct {
IdToken string `json:"id_token"`
@ -280,11 +293,11 @@ func (h *OAuthHandler) tokenEndpoint(w http.ResponseWriter, r *http.Request) {
}
response := Response{
IdToken: tokens.ID.Token,
IdToken: id.Token,
TokenType: "Bearer",
AccessToken: tokens.Access.Token,
RefreshToken: tokens.Refresh.Token,
ExpiresIn: tokens.Access.Expiration,
AccessToken: access.Token,
RefreshToken: refresh.Token,
ExpiresIn: access.ExpiresAt.Sub(time.Now()).Seconds(),
}
log.Printf("DEBUG: refresh - sending following response: %#v\n", response)