resolver_impl.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. package graphql
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/Nerzal/gocloak/v11"
  6. "github.com/golang-jwt/jwt/v4"
  7. "github.com/gshopify/service-wrapper/auth"
  8. "github.com/gshopify/service-wrapper/config"
  9. "github.com/mitchellh/mapstructure"
  10. "gshopper.com/gshopify/customer/graphql/generated"
  11. m "gshopper.com/gshopify/customer/model"
  12. "sync"
  13. "time"
  14. )
  15. type Resolver struct {
  16. conf *auth.Config
  17. client gocloak.GoCloak
  18. mu sync.Mutex
  19. _token *gocloak.JWT
  20. _expiresAt time.Time
  21. }
  22. func NewResolver() (*Resolver, error) {
  23. r := &Resolver{
  24. conf: auth.New(),
  25. }
  26. if err := config.Instance().Load(context.Background(), r.conf); err != nil {
  27. return nil, err
  28. }
  29. r.client = gocloak.NewClient(r.conf.Endpoint)
  30. return r, nil
  31. }
  32. func (r *Resolver) decodeAccessToken(ctx context.Context, t string) (*jwt.Token, string, error) {
  33. if t == "" {
  34. return nil, "", fmt.Errorf("could not decode accessToken: Token is empty")
  35. }
  36. token, claim, err := r.client.DecodeAccessToken(ctx, t, r.conf.Cli.Realm)
  37. if err != nil {
  38. return nil, "", err
  39. }
  40. if !token.Valid {
  41. return nil, "", fmt.Errorf("could not decode accessToken: Token is NOT valid")
  42. }
  43. var sessionId string
  44. if claimed, ok := (*claim)["sid"]; ok {
  45. if s, ok := claimed.(string); ok {
  46. sessionId = s
  47. }
  48. }
  49. if sessionId == "" {
  50. return nil, "", fmt.Errorf("could not claim session id")
  51. }
  52. return token, sessionId, nil
  53. }
  54. func (r *Resolver) customer(ctx context.Context, t string) (*generated.Customer, error) {
  55. var (
  56. customer = generated.Customer{}
  57. udata map[string]any
  58. phone *m.Phone
  59. err error
  60. )
  61. udata, err = r.client.GetRawUserInfo(ctx, t, r.conf.Cli.Realm)
  62. if err != nil {
  63. return nil, err
  64. }
  65. if err = mapstructure.Decode(udata, &customer); err != nil {
  66. return nil, err
  67. }
  68. customer.Phone = nil
  69. phone, err = m.ParsePhoneNumber(udata)
  70. if err != nil {
  71. return nil, err
  72. }
  73. customer.Phone = gocloak.StringP(phone.String())
  74. customer.Metafields = append(customer.Metafields,
  75. &generated.Metafield{
  76. Namespace: "customer",
  77. Key: "phone_region",
  78. Type: "single_line_text_field",
  79. Value: phone.PhoneRegion,
  80. },
  81. &generated.Metafield{
  82. Namespace: "customer",
  83. Key: "phone_verified",
  84. Type: "boolean",
  85. Value: fmt.Sprintf("%v", phone.Verified),
  86. })
  87. return &customer, nil
  88. }
  89. func (r *Resolver) admin(ctx context.Context) (*gocloak.JWT, error) {
  90. var (
  91. creds = r.conf.Admin
  92. err error
  93. )
  94. if time.Now().After(r._expiresAt) {
  95. r.mu.Lock()
  96. defer r.mu.Unlock()
  97. if r._token != nil {
  98. r._token, err = r.client.RefreshToken(ctx, r._token.RefreshToken, creds.ClientId, creds.ClientSecret, creds.Realm)
  99. }
  100. if r._token == nil || err != nil {
  101. r._token, err = r.client.LoginAdmin(ctx, creds.Login, creds.Password, creds.Realm)
  102. }
  103. if r._token == nil || err != nil {
  104. r._expiresAt = time.Time{}
  105. } else {
  106. r._expiresAt = time.Now().
  107. Add(time.Duration(r._token.ExpiresIn) * time.Second).
  108. Add(-time.Second * 5)
  109. }
  110. }
  111. return r._token, err
  112. }
  113. func (r *Resolver) saveSession(ctx context.Context, token *gocloak.JWT) error {
  114. return auth.SessionManager().PutToken(
  115. ctx,
  116. token.SessionState,
  117. token.RefreshToken,
  118. time.Duration(token.RefreshExpiresIn)*time.Second)
  119. }
  120. // Mutation returns generated.MutationResolver implementation.
  121. func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} }
  122. // Query returns generated.QueryResolver implementation.
  123. func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
  124. type mutationResolver struct{ *Resolver }
  125. type queryResolver struct{ *Resolver }