Add Exchange Code; Add Winterfest Lobby )mcp coming soon!) remove tokens from db and revert to jwt tokens

This commit is contained in:
Eccentric 2023-12-26 03:07:18 +00:00
parent 0a3964805d
commit 5c9bbb2f89
10 changed files with 220 additions and 97 deletions

View File

@ -46,6 +46,20 @@ func (k *keyPair) DecryptAndVerify(encryptedMessage []byte, signature []byte) []
return decryptedMessage
}
func (k *keyPair) DecryptAndVerifyB64(encryptedMessage string, signature string) ([]byte, bool) {
encryptedMessageBytes, err := Base64Decode(encryptedMessage)
if err {
return []byte{}, true
}
signatureBytes, err := Base64Decode(signature)
if err {
return []byte{}, true
}
return k.DecryptAndVerify(encryptedMessageBytes, signatureBytes), false
}
func (k *keyPair) ExportPrivateKey() []byte {
privateKey := x509.MarshalPKCS1PrivateKey(&k.PrivateKey)
return privateKey

44
aid/token.go Normal file
View File

@ -0,0 +1,44 @@
package aid
import (
"fmt"
"github.com/golang-jwt/jwt/v5"
)
func JWTSign(m JSON) (string, error) {
claims := jwt.MapClaims{}
for k, v := range m {
claims[k] = v
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(Config.JWT.Secret))
}
func JWTVerify(tokenString string) (JSON, error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return []byte(Config.JWT.Secret), nil
})
if err != nil {
return nil, err
}
if !token.Valid {
return nil, fmt.Errorf("invalid token")
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
return nil, fmt.Errorf("invalid claims")
}
json := JSON{}
for k, v := range claims {
json[k] = v
}
return json, nil
}

2
go.mod
View File

@ -5,7 +5,7 @@ go 1.21.3
require (
github.com/goccy/go-json v0.10.2
github.com/gofiber/fiber/v2 v2.50.0
github.com/golang-jwt/jwt/v5 v5.0.0
github.com/golang-jwt/jwt/v5 v5.2.0
github.com/google/uuid v1.4.0
github.com/lib/pq v1.10.9
github.com/r3labs/diff/v3 v3.0.1

2
go.sum
View File

@ -97,6 +97,8 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=

View File

@ -6,7 +6,6 @@ import (
"github.com/ectrc/snow/aid"
p "github.com/ectrc/snow/person"
"github.com/ectrc/snow/storage"
"github.com/gofiber/fiber/v2"
)
@ -14,13 +13,16 @@ var (
oauthTokenGrantTypes = map[string]func(c *fiber.Ctx, body *FortniteTokenBody) error{
"client_credentials": PostTokenClientCredentials,
"password": PostTokenPassword,
"exchange_code": PostTokenExchangeCode,
}
)
type FortniteTokenBody struct {
GrantType string `form:"grant_type" binding:"required"`
ExchangeCode string `form:"exchange_code"`
Username string `form:"username"`
Password string `form:"password"`
TokenType string `form:"token_type"`
}
func PostFortniteToken(c *fiber.Ctx) error {
@ -42,18 +44,87 @@ func PostTokenClientCredentials(c *fiber.Ctx, body *FortniteTokenBody) error {
hash := aid.Hash([]byte(client + "." + sig))
return c.Status(fiber.StatusOK).JSON(aid.JSON{
"access_token": hash,
"access_token": "eg1~" + hash,
"token_type": "bearer",
"client_id": c.IP(),
"client_service": "fortnite",
"internal_client": true,
"expires_in": 3600,
"expires_at": time.Now().Add(time.Hour).Format("2006-01-02T15:04:05.999Z"),
"product_id": "prod-fn",
"sandbox_id": "fn",
})
}
func PostTokenExchangeCode(c *fiber.Ctx, body *FortniteTokenBody) error {
if body.ExchangeCode == "" {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Exchange Code is empty"))
}
codeParts := strings.Split(body.ExchangeCode, ".")
if len(codeParts) != 2 {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Exchange Code"))
}
code, failed := aid.KeyPair.DecryptAndVerifyB64(codeParts[0], codeParts[1])
if failed {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Exchange Code"))
}
personParts := strings.Split(string(code), "=")
if len(personParts) != 2 {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Exchange Code"))
}
personId := personParts[0]
expire, err := time.Parse("2006-01-02T15:04:05.999Z", personParts[1])
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Exchange Code"))
}
if expire.Add(time.Hour).Before(time.Now()) {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Exchange Code"))
}
person := p.Find(personId)
if person == nil {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Exchange Code"))
}
access, err := aid.JWTSign(aid.JSON{
"snow_id": person.ID, // custom
"creation_date": time.Now().Format("2006-01-02T15:04:05.999Z"),
})
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(aid.ErrorInternalServer)
}
refresh, err := aid.JWTSign(aid.JSON{
"snow_id": person.ID,
"creation_date": time.Now().Format("2006-01-02T15:04:05.999Z"),
})
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(aid.ErrorInternalServer)
}
return c.Status(fiber.StatusOK).JSON(aid.JSON{
"access_token": "eg1~" + access,
"account_id": person.ID,
"client_id": c.IP(),
"client_service": "fortnite",
"app": "fortnite",
"device_id": "default",
"display_name": person.DisplayName,
"expires_at": time.Now().Add(time.Hour * 24).Format("2006-01-02T15:04:05.999Z"),
"expires_in": 86400,
"internal_client": true,
"refresh_expires": 86400,
"refresh_expires_at": time.Now().Add(time.Hour * 24).Format("2006-01-02T15:04:05.999Z"),
"refresh_token": "eg1~" + refresh,
"token_type": "bearer",
"product_id": "prod-fn",
"sandbox_id": "fn",
})
}
func PostTokenPassword(c *fiber.Ctx, body *FortniteTokenBody) error {
if body.Username == "" || body.Password == "" {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Username/Password is empty"))
@ -64,31 +135,24 @@ func PostTokenPassword(c *fiber.Ctx, body *FortniteTokenBody) error {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("No Account Found"))
}
access, ac_sig := aid.KeyPair.EncryptAndSignB64([]byte(person.ID))
ac_hash := aid.Hash([]byte(access + "." + ac_sig))
ac_token := &storage.DB_GameToken{
ID: ac_hash,
PersonID: person.ID,
AccessToken: access + "." + ac_sig,
Type: "access",
access, err := aid.JWTSign(aid.JSON{
"snow_id": person.ID, // custom
"creation_date": time.Now().Format("2006-01-02T15:04:05.999Z"),
})
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(aid.ErrorInternalServer)
}
storage.Repo.SaveToken(ac_token)
refresh, re_sig := aid.KeyPair.EncryptAndSignB64([]byte(person.ID))
re_hash := aid.Hash([]byte(refresh + "." + re_sig))
re_token := &storage.DB_GameToken{
ID: re_hash,
PersonID: person.ID,
AccessToken: refresh + "." + re_sig,
Type: "refresh",
refresh, err := aid.JWTSign(aid.JSON{
"snow_id": person.ID,
"creation_date": time.Now().Format("2006-01-02T15:04:05.999Z"),
})
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(aid.ErrorInternalServer)
}
storage.Repo.SaveToken(re_token)
return c.Status(fiber.StatusOK).JSON(aid.JSON{
// "access_token": access + "." + ac_sig,
"access_token": ac_hash,
"access_token": "eg1~" + access,
"account_id": person.ID,
"client_id": c.IP(),
"client_service": "fortnite",
@ -100,8 +164,7 @@ func PostTokenPassword(c *fiber.Ctx, body *FortniteTokenBody) error {
"internal_client": true,
"refresh_expires": 86400,
"refresh_expires_at": time.Now().Add(time.Hour * 24).Format("2006-01-02T15:04:05.999Z"),
// "refresh_token": refresh + "." + re_sig,
"refresh_token": re_hash,
"refresh_token": "eg1~" + refresh,
"token_type": "bearer",
"product_id": "prod-fn",
"sandbox_id": "fn",
@ -113,13 +176,21 @@ func GetOAuthVerify(c *fiber.Ctx) error {
if auth == "" {
return c.Status(fiber.StatusForbidden).JSON(aid.ErrorBadRequest("Authorization Header is empty"))
}
real := strings.ReplaceAll(auth, "bearer ", "")
real := strings.ReplaceAll(auth, "bearer eg1~", "")
found := storage.Repo.GetToken(real)
if found == nil {
claims, err := aid.JWTVerify(real)
if err != nil {
return c.Status(fiber.StatusForbidden).JSON(aid.ErrorBadRequest("Invalid Access Token"))
}
if claims["snow_id"] == nil {
return c.Status(fiber.StatusForbidden).JSON(aid.ErrorBadRequest("Invalid Access Token"))
}
snowId, ok := claims["snow_id"].(string)
if !ok {
return c.Status(fiber.StatusForbidden).JSON(aid.ErrorBadRequest("Invalid Access Token"))
}
snowId := found.PersonID
person := p.Find(snowId)
if person == nil {
@ -150,13 +221,21 @@ func MiddlewareFortnite(c *fiber.Ctx) error {
if auth == "" {
return c.Status(fiber.StatusForbidden).JSON(aid.ErrorBadRequest("Authorization Header is empty"))
}
real := strings.ReplaceAll(auth, "bearer ", "")
real := strings.ReplaceAll(auth, "bearer eg1~", "")
found := storage.Repo.GetToken(real)
if found == nil {
claims, err := aid.JWTVerify(real)
if err != nil {
return c.Status(fiber.StatusForbidden).JSON(aid.ErrorBadRequest("Invalid Access Token"))
}
if claims["snow_id"] == nil {
return c.Status(fiber.StatusForbidden).JSON(aid.ErrorBadRequest("Invalid Access Token"))
}
snowId, ok := claims["snow_id"].(string)
if !ok {
return c.Status(fiber.StatusForbidden).JSON(aid.ErrorBadRequest("Invalid Access Token"))
}
snowId := found.PersonID
person := p.Find(snowId)
if person == nil {
@ -173,11 +252,23 @@ func MiddlewareWeb(c *fiber.Ctx) error {
return c.Status(fiber.StatusForbidden).JSON(aid.ErrorBadRequest("Authorization Header is empty"))
}
found := storage.Repo.GetToken(auth)
if found == nil {
claims, err := aid.JWTVerify(auth)
if err != nil {
return c.Status(fiber.StatusForbidden).JSON(aid.ErrorBadRequest("Invalid Access Token"))
}
if claims["snow_id"] == nil {
return c.Status(fiber.StatusForbidden).JSON(aid.ErrorBadRequest("Invalid Access Token"))
}
if claims["frontend"] == nil {
return c.Status(fiber.StatusForbidden).JSON(aid.ErrorBadRequest("Invalid Claims"))
}
snowId, ok := claims["snow_id"].(string)
if !ok {
return c.Status(fiber.StatusForbidden).JSON(aid.ErrorBadRequest("Invalid Access Token"))
}
snowId := found.PersonID
person := p.Find(snowId)
if person == nil {

View File

@ -154,6 +154,7 @@ func PostAssets(c *fiber.Ctx) error {
func GetContentPages(c *fiber.Ctx) error {
seasonString := strconv.Itoa(aid.Config.Fortnite.Season)
playlists := []aid.JSON{}
for playlist := range fortnite.PlaylistImages {
playlists = append(playlists, aid.JSON{
@ -163,6 +164,20 @@ func GetContentPages(c *fiber.Ctx) error {
})
}
backgrounds := []aid.JSON{}
switch aid.Config.Fortnite.Season {
case 11:
backgrounds = append(backgrounds, aid.JSON{
"key": "lobby",
"stage": "Winter19",
})
default:
backgrounds = append(backgrounds, aid.JSON{
"key": "lobby",
"stage": "season" + seasonString,
})
}
return c.Status(fiber.StatusOK).JSON(aid.JSON{
"subgameselectdata": aid.JSON{
"saveTheWorldUnowned": aid.JSON{
@ -195,16 +210,7 @@ func GetContentPages(c *fiber.Ctx) error {
"lastModified": "0000-00-00T00:00:00.000Z",
},
"dynamicbackgrounds": aid.JSON{
"backgrounds": aid.JSON{"backgrounds": []aid.JSON{
{
"key": "lobby",
"stage": "season" + seasonString,
},
{
"key": "vault",
"stage": "season" + seasonString,
},
}},
"backgrounds": aid.JSON{"backgrounds": backgrounds},
"lastModified": "0000-00-00T00:00:00.000Z",
},
"shopSections": aid.JSON{

View File

@ -66,6 +66,20 @@ func GetFortniteTimeline(c *fiber.Ctx) error {
"activeSince": "0001-01-01T00:00:00Z",
"eventType": "EventFlag.LobbySeason6Halloween",
})
case 11:
events = append(events, aid.JSON{
"activeUntil": "9999-01-01T00:00:00.000Z",
"activeSince": "0001-01-01T00:00:00Z",
"eventType": "EventFlag.LTE_WinterFest2019",
}, aid.JSON{
"activeUntil": "9999-01-01T00:00:00.000Z",
"activeSince": "0001-01-01T00:00:00Z",
"eventType": "EventFlag.LTE_WinterFest",
}, aid.JSON{
"activeUntil": "9999-01-01T00:00:00.000Z",
"activeSince": "0001-01-01T00:00:00Z",
"eventType": "EventFlag.Winterfest.Tree",
})
default:
events = append(events, aid.JSON{
"activeUntil": "9999-12-31T23:59:59.999Z",

View File

@ -46,7 +46,6 @@ func (s *PostgresStorage) MigrateAll() {
s.Migrate(&DB_TemporaryCode{}, "ExchangeCodes")
s.Migrate(&DB_DiscordPerson{}, "Discords")
s.Migrate(&DB_SeasonStat{}, "Stats")
s.Migrate(&DB_GameToken{}, "GameTokens")
}
func (s *PostgresStorage) DropTables() {
@ -243,22 +242,3 @@ func (s *PostgresStorage) SaveDiscordPerson(discordPerson *DB_DiscordPerson) {
func (s *PostgresStorage) DeleteDiscordPerson(discordPersonId string) {
s.Postgres.Delete(&DB_DiscordPerson{}, "id = ?", discordPersonId)
}
func (s *PostgresStorage) SaveToken(token *DB_GameToken) {
s.Postgres.Save(token)
}
func (s *PostgresStorage) DeleteToken(tokenId string) {
s.Postgres.Delete(&DB_GameToken{}, "id = ?", tokenId)
}
func (s *PostgresStorage) GetToken(tokenId string) *DB_GameToken {
var token DB_GameToken
s.Postgres.Model(&DB_GameToken{}).Where("id = ?", tokenId).Find(&token)
if token.ID == "" {
return nil
}
return &token
}

View File

@ -48,10 +48,6 @@ type Storage interface {
SaveDiscordPerson(person *DB_DiscordPerson)
DeleteDiscordPerson(personId string)
SaveToken(token *DB_GameToken)
GetToken(tokenId string) *DB_GameToken
DeleteToken(tokenId string)
}
type Repository struct {
@ -194,15 +190,3 @@ func (r *Repository) SaveDiscordPerson(person *DB_DiscordPerson) {
func (r *Repository) DeleteDiscordPerson(personId string) {
r.Storage.DeleteDiscordPerson(personId)
}
func (r *Repository) SaveToken(token *DB_GameToken) {
r.Storage.SaveToken(token)
}
func (r *Repository) GetToken(tokenId string) *DB_GameToken {
return r.Storage.GetToken(tokenId)
}
func (r *Repository) DeleteToken(tokenId string) {
r.Storage.DeleteToken(tokenId)
}

View File

@ -178,15 +178,3 @@ type DB_SeasonStat struct {
func (DB_SeasonStat) TableName() string {
return "Stats"
}
type DB_GameToken struct {
ID string `gorm:"primary_key"`
PersonID string
AccessToken string
Type string
ExpiresAt int64
}
func (DB_GameToken) TableName() string {
return "GameTokens"
}