feat: create service session
This commit is contained in:
@ -17,20 +17,10 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ApiToken struct {
|
func (h *OAuthHandler) signApiTokens(user *repository.User, apiService *repository.ApiService, nonce *string) (*types.SignedToken, *types.SignedToken, *types.SignedToken, error) {
|
||||||
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) {
|
|
||||||
accessExpiresIn := 15 * time.Minute
|
accessExpiresIn := 15 * time.Minute
|
||||||
accessExpiresAt := time.Now().Add(accessExpiresIn)
|
accessExpiresAt := time.Now().Add(accessExpiresIn)
|
||||||
|
accessJTI := uuid.New()
|
||||||
|
|
||||||
accessClaims := types.ApiClaims{
|
accessClaims := types.ApiClaims{
|
||||||
Permissions: []string{},
|
Permissions: []string{},
|
||||||
@ -40,12 +30,13 @@ func (h *OAuthHandler) signApiTokens(user *repository.User, apiService *reposito
|
|||||||
Audience: jwt.ClaimStrings{apiService.ClientID},
|
Audience: jwt.ClaimStrings{apiService.ClientID},
|
||||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||||
ExpiresAt: jwt.NewNumericDate(accessExpiresAt),
|
ExpiresAt: jwt.NewNumericDate(accessExpiresAt),
|
||||||
|
ID: accessJTI.String(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
access, err := util.SignJwtToken(accessClaims, h.cfg.Jwt.PrivateKey)
|
access, err := util.SignJwtToken(accessClaims, h.cfg.Jwt.PrivateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var roles = []string{"user"}
|
var roles = []string{"user"}
|
||||||
@ -56,6 +47,7 @@ func (h *OAuthHandler) signApiTokens(user *repository.User, apiService *reposito
|
|||||||
|
|
||||||
idExpiresIn := 15 * time.Minute
|
idExpiresIn := 15 * time.Minute
|
||||||
idExpiresAt := time.Now().Add(idExpiresIn)
|
idExpiresAt := time.Now().Add(idExpiresIn)
|
||||||
|
idJTI := uuid.New()
|
||||||
|
|
||||||
idClaims := types.IdTokenClaims{
|
idClaims := types.IdTokenClaims{
|
||||||
Email: user.Email,
|
Email: user.Email,
|
||||||
@ -70,16 +62,18 @@ func (h *OAuthHandler) signApiTokens(user *repository.User, apiService *reposito
|
|||||||
Audience: jwt.ClaimStrings{apiService.ClientID},
|
Audience: jwt.ClaimStrings{apiService.ClientID},
|
||||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||||
ExpiresAt: jwt.NewNumericDate(idExpiresAt),
|
ExpiresAt: jwt.NewNumericDate(idExpiresAt),
|
||||||
|
ID: idJTI.String(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
idToken, err := util.SignJwtToken(idClaims, h.cfg.Jwt.PrivateKey)
|
idToken, err := util.SignJwtToken(idClaims, h.cfg.Jwt.PrivateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshExpiresIn := 24 * time.Hour
|
refreshExpiresIn := 24 * time.Hour
|
||||||
refreshExpiresAt := time.Now().Add(refreshExpiresIn)
|
refreshExpiresAt := time.Now().Add(refreshExpiresIn)
|
||||||
|
refreshJTI := uuid.New()
|
||||||
|
|
||||||
refreshClaims := types.ApiRefreshClaims{
|
refreshClaims := types.ApiRefreshClaims{
|
||||||
UserID: user.ID.String(),
|
UserID: user.ID.String(),
|
||||||
@ -89,28 +83,16 @@ func (h *OAuthHandler) signApiTokens(user *repository.User, apiService *reposito
|
|||||||
Audience: jwt.ClaimStrings{apiService.ClientID},
|
Audience: jwt.ClaimStrings{apiService.ClientID},
|
||||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||||
ExpiresAt: jwt.NewNumericDate(refreshExpiresAt),
|
ExpiresAt: jwt.NewNumericDate(refreshExpiresAt),
|
||||||
|
ID: refreshJTI.String(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh, err := util.SignJwtToken(refreshClaims, h.cfg.Jwt.PrivateKey)
|
refresh, err := util.SignJwtToken(refreshClaims, h.cfg.Jwt.PrivateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ApiTokens{
|
return types.NewSignedToken(idToken, idExpiresAt, idJTI), types.NewSignedToken(access, accessExpiresAt, accessJTI), types.NewSignedToken(refresh, refreshExpiresAt, refreshJTI), nil
|
||||||
ID: ApiToken{
|
|
||||||
Token: idToken,
|
|
||||||
Expiration: idExpiresIn.Seconds(),
|
|
||||||
},
|
|
||||||
Access: ApiToken{
|
|
||||||
Token: access,
|
|
||||||
Expiration: accessExpiresIn.Seconds(),
|
|
||||||
},
|
|
||||||
Refresh: ApiToken{
|
|
||||||
Token: refresh,
|
|
||||||
Expiration: refreshExpiresIn.Seconds(),
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *OAuthHandler) tokenEndpoint(w http.ResponseWriter, r *http.Request) {
|
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)
|
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 {
|
if err != nil {
|
||||||
log.Printf("ERR: Failed to find session under the code %s: %v\n", code, err)
|
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)
|
web.Error(w, "no session found under this auth code", http.StatusNotFound)
|
||||||
return
|
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 {
|
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)
|
web.Error(w, "service is not registered", http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if session.ClientID != clientId {
|
if codeSession.ClientID != clientId {
|
||||||
web.Error(w, "invalid auth", http.StatusUnauthorized)
|
web.Error(w, "invalid auth", http.StatusUnauthorized)
|
||||||
return
|
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 {
|
if err != nil {
|
||||||
web.Error(w, "requested user not found", http.StatusNotFound)
|
web.Error(w, "requested user not found", http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tokens, err := h.signApiTokens(&user, &apiService, &session.Nonce)
|
id, access, refresh, err := h.signApiTokens(&user, &apiService, &codeSession.Nonce)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("ERR: Failed to sign api tokens:", err)
|
log.Println("ERR: Failed to sign api tokens:", err)
|
||||||
web.Error(w, "failed to sign tokens", http.StatusInternalServerError)
|
web.Error(w, "failed to sign tokens", http.StatusInternalServerError)
|
||||||
return
|
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 {
|
type Response struct {
|
||||||
IdToken string `json:"id_token"`
|
IdToken string `json:"id_token"`
|
||||||
TokenType string `json:"token_type"`
|
TokenType string `json:"token_type"`
|
||||||
@ -219,11 +232,11 @@ func (h *OAuthHandler) tokenEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
response := Response{
|
response := Response{
|
||||||
IdToken: tokens.ID.Token,
|
IdToken: id.Token,
|
||||||
TokenType: "Bearer",
|
TokenType: "Bearer",
|
||||||
AccessToken: tokens.Access.Token,
|
AccessToken: access.Token,
|
||||||
RefreshToken: tokens.Refresh.Token,
|
RefreshToken: refresh.Token,
|
||||||
ExpiresIn: tokens.Access.Expiration,
|
ExpiresIn: access.ExpiresAt.Sub(time.Now()).Seconds(),
|
||||||
Email: user.Email,
|
Email: user.Email,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +282,7 @@ func (h *OAuthHandler) tokenEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tokens, err := h.signApiTokens(&user, &apiService, nil)
|
id, access, refresh, err := h.signApiTokens(&user, &apiService, nil)
|
||||||
|
|
||||||
type Response struct {
|
type Response struct {
|
||||||
IdToken string `json:"id_token"`
|
IdToken string `json:"id_token"`
|
||||||
@ -280,11 +293,11 @@ func (h *OAuthHandler) tokenEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
response := Response{
|
response := Response{
|
||||||
IdToken: tokens.ID.Token,
|
IdToken: id.Token,
|
||||||
TokenType: "Bearer",
|
TokenType: "Bearer",
|
||||||
AccessToken: tokens.Access.Token,
|
AccessToken: access.Token,
|
||||||
RefreshToken: tokens.Refresh.Token,
|
RefreshToken: refresh.Token,
|
||||||
ExpiresIn: tokens.Access.Expiration,
|
ExpiresIn: access.ExpiresAt.Sub(time.Now()).Seconds(),
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("DEBUG: refresh - sending following response: %#v\n", response)
|
log.Printf("DEBUG: refresh - sending following response: %#v\n", response)
|
||||||
|
Reference in New Issue
Block a user