2024-04-20 11:04:33 +00:00
|
|
|
package services
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2024-07-28 12:04:22 +00:00
|
|
|
"strconv"
|
|
|
|
"time"
|
|
|
|
|
2024-06-17 14:21:34 +00:00
|
|
|
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
|
|
|
|
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
|
2024-04-20 11:04:33 +00:00
|
|
|
"github.com/samber/lo"
|
|
|
|
"github.com/spf13/viper"
|
|
|
|
)
|
|
|
|
|
2024-07-28 12:04:22 +00:00
|
|
|
func GetToken(ticket models.AuthTicket) (atk, rtk string, err error) {
|
|
|
|
if err = ticket.IsAvailable(); err != nil {
|
|
|
|
return
|
2024-04-20 11:04:33 +00:00
|
|
|
}
|
|
|
|
if ticket.AccessToken == nil || ticket.RefreshToken == nil {
|
2024-07-28 12:04:22 +00:00
|
|
|
err = fmt.Errorf("unable to encode token, access or refresh token id missing")
|
|
|
|
return
|
2024-04-20 11:04:33 +00:00
|
|
|
}
|
|
|
|
|
2024-07-28 12:04:22 +00:00
|
|
|
atkDeadline := time.Duration(viper.GetInt64("security.access_token_duration")) * time.Second
|
|
|
|
rtkDeadline := time.Duration(viper.GetInt64("security.refresh_token_duration")) * time.Second
|
2024-04-20 11:04:33 +00:00
|
|
|
|
|
|
|
sub := strconv.Itoa(int(ticket.AccountID))
|
|
|
|
sed := strconv.Itoa(int(ticket.ID))
|
2024-07-28 14:30:51 +00:00
|
|
|
atk, err = EncodeJwt(*ticket.AccessToken, JwtAccessType, sub, sed, nil, ticket.Audiences, time.Now().Add(atkDeadline))
|
2024-04-20 11:04:33 +00:00
|
|
|
if err != nil {
|
2024-07-28 12:04:22 +00:00
|
|
|
return
|
2024-04-20 11:04:33 +00:00
|
|
|
}
|
2024-07-28 14:30:51 +00:00
|
|
|
rtk, err = EncodeJwt(*ticket.RefreshToken, JwtRefreshType, sub, sed, nil, ticket.Audiences, time.Now().Add(rtkDeadline))
|
2024-04-20 11:04:33 +00:00
|
|
|
if err != nil {
|
2024-07-28 12:04:22 +00:00
|
|
|
return
|
2024-04-20 11:04:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ticket.LastGrantAt = lo.ToPtr(time.Now())
|
|
|
|
database.C.Save(&ticket)
|
|
|
|
|
2024-07-28 12:04:22 +00:00
|
|
|
return
|
2024-04-20 11:04:33 +00:00
|
|
|
}
|
|
|
|
|
2024-07-28 12:04:22 +00:00
|
|
|
func ExchangeToken(token string) (atk, rtk string, err error) {
|
2024-04-20 11:04:33 +00:00
|
|
|
var ticket models.AuthTicket
|
2024-07-28 12:04:22 +00:00
|
|
|
if err = database.C.Where(models.AuthTicket{GrantToken: &token}).First(&ticket).Error; err != nil {
|
|
|
|
return
|
2024-04-20 11:04:33 +00:00
|
|
|
} else if ticket.LastGrantAt != nil {
|
2024-07-28 12:04:22 +00:00
|
|
|
err = fmt.Errorf("ticket was granted the first token, use refresh token instead")
|
|
|
|
return
|
2024-04-20 11:04:33 +00:00
|
|
|
} else if len(ticket.Audiences) > 1 {
|
2024-07-28 12:04:22 +00:00
|
|
|
err = fmt.Errorf("should use authorization code grant type")
|
|
|
|
return
|
2024-04-20 11:04:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return GetToken(ticket)
|
|
|
|
}
|
|
|
|
|
2024-07-28 12:04:22 +00:00
|
|
|
func ExchangeOauthToken(clientId, clientSecret, redirectUri, token string) (idk, atk, rtk string, err error) {
|
2024-04-20 11:04:33 +00:00
|
|
|
var client models.ThirdClient
|
2024-07-28 12:04:22 +00:00
|
|
|
if err = database.C.Where(models.ThirdClient{Alias: clientId}).First(&client).Error; err != nil {
|
|
|
|
return
|
2024-04-20 11:04:33 +00:00
|
|
|
} else if client.Secret != clientSecret {
|
2024-07-28 12:04:22 +00:00
|
|
|
err = fmt.Errorf("invalid client secret")
|
|
|
|
return
|
2024-04-20 11:04:33 +00:00
|
|
|
} else if !client.IsDraft && !lo.Contains(client.Callbacks, redirectUri) {
|
2024-07-28 12:04:22 +00:00
|
|
|
err = fmt.Errorf("invalid redirect uri")
|
|
|
|
return
|
2024-04-20 11:04:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var ticket models.AuthTicket
|
2024-07-28 12:04:22 +00:00
|
|
|
if err = database.C.Where(models.AuthTicket{GrantToken: &token}).First(&ticket).Error; err != nil {
|
|
|
|
return
|
2024-04-20 11:04:33 +00:00
|
|
|
} else if ticket.LastGrantAt != nil {
|
2024-07-28 12:04:22 +00:00
|
|
|
err = fmt.Errorf("ticket was granted the first token, use refresh token instead")
|
|
|
|
return
|
2024-04-20 11:04:33 +00:00
|
|
|
}
|
|
|
|
|
2024-07-28 12:04:22 +00:00
|
|
|
atk, rtk, err = GetToken(ticket)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var user models.Account
|
|
|
|
if err = database.C.Where(models.Account{
|
|
|
|
BaseModel: models.BaseModel{ID: ticket.AccountID},
|
|
|
|
}).Preload("Contacts").First(&user).Error; err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
sub := strconv.Itoa(int(ticket.AccountID))
|
|
|
|
sed := strconv.Itoa(int(ticket.ID))
|
2024-07-28 14:30:51 +00:00
|
|
|
idk, err = EncodeJwt(*ticket.AccessToken, JwtAccessType, sub, sed, ticket.Nonce, ticket.Audiences, time.Now().Add(24*time.Minute), user)
|
2024-07-28 12:04:22 +00:00
|
|
|
|
|
|
|
return
|
2024-04-20 11:04:33 +00:00
|
|
|
}
|
|
|
|
|
2024-07-28 12:04:22 +00:00
|
|
|
func RefreshToken(token string) (atk, rtk string, err error) {
|
2024-04-20 11:04:33 +00:00
|
|
|
parseInt := func(str string) int {
|
|
|
|
val, _ := strconv.Atoi(str)
|
|
|
|
return val
|
|
|
|
}
|
|
|
|
|
|
|
|
var ticket models.AuthTicket
|
2024-07-28 12:04:22 +00:00
|
|
|
var claims PayloadClaims
|
|
|
|
if claims, err = DecodeJwt(token); err != nil {
|
|
|
|
return
|
2024-04-20 11:04:33 +00:00
|
|
|
} else if claims.Type != JwtRefreshType {
|
2024-07-28 12:04:22 +00:00
|
|
|
err = fmt.Errorf("invalid token type, expected refresh token")
|
|
|
|
return
|
|
|
|
} else if err = database.C.Where(models.AuthTicket{
|
2024-04-20 11:04:33 +00:00
|
|
|
BaseModel: models.BaseModel{ID: uint(parseInt(claims.SessionID))},
|
|
|
|
}).First(&ticket).Error; err != nil {
|
2024-07-28 12:04:22 +00:00
|
|
|
return
|
2024-04-20 11:04:33 +00:00
|
|
|
}
|
|
|
|
|
2024-07-28 12:04:22 +00:00
|
|
|
if ticket, err = RegenSession(ticket); err != nil {
|
|
|
|
return
|
2024-04-20 11:04:33 +00:00
|
|
|
} else {
|
|
|
|
return GetToken(ticket)
|
|
|
|
}
|
|
|
|
}
|