From 41d439beab457056a30108c4309b57f22cd95c91 Mon Sep 17 00:00:00 2001 From: LandaMm Date: Sun, 15 Jun 2025 21:02:38 +0200 Subject: [PATCH] feat: create service session --- internal/oauth/token.go | 103 ++++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 45 deletions(-) diff --git a/internal/oauth/token.go b/internal/oauth/token.go index d11e16c..2ab7e2f 100644 --- a/internal/oauth/token.go +++ b/internal/oauth/token.go @@ -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)