123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- package graphql
- import (
- "context"
- "fmt"
- "github.com/Nerzal/gocloak/v11"
- "github.com/golang-jwt/jwt/v4"
- "github.com/gshopify/service-wrapper/auth"
- "github.com/gshopify/service-wrapper/config"
- "github.com/mitchellh/mapstructure"
- "gshopper.com/gshopify/customer/graphql/generated"
- m "gshopper.com/gshopify/customer/model"
- "sync"
- "time"
- )
- type Resolver struct {
- conf *auth.Config
- client gocloak.GoCloak
- mu sync.Mutex
- _token *gocloak.JWT
- _expiresAt time.Time
- }
- func NewResolver() (*Resolver, error) {
- r := &Resolver{
- conf: auth.New(),
- }
- if err := config.Instance().Load(context.Background(), r.conf); err != nil {
- return nil, err
- }
- r.client = gocloak.NewClient(r.conf.Endpoint)
- return r, nil
- }
- func (r *Resolver) decodeAccessToken(ctx context.Context, t string) (*jwt.Token, string, error) {
- if t == "" {
- return nil, "", fmt.Errorf("could not decode accessToken: Token is empty")
- }
- token, claim, err := r.client.DecodeAccessToken(ctx, t, r.conf.Cli.Realm)
- if err != nil {
- return nil, "", err
- }
- if !token.Valid {
- return nil, "", fmt.Errorf("could not decode accessToken: Token is NOT valid")
- }
- var sessionId string
- if claimed, ok := (*claim)["sid"]; ok {
- if s, ok := claimed.(string); ok {
- sessionId = s
- }
- }
- if sessionId == "" {
- return nil, "", fmt.Errorf("could not claim session id")
- }
- return token, sessionId, nil
- }
- func (r *Resolver) customer(ctx context.Context, t string) (*generated.Customer, error) {
- var (
- customer = generated.Customer{}
- udata map[string]any
- phone *m.Phone
- err error
- )
- udata, err = r.client.GetRawUserInfo(ctx, t, r.conf.Cli.Realm)
- if err != nil {
- return nil, err
- }
- if err = mapstructure.Decode(udata, &customer); err != nil {
- return nil, err
- }
- customer.Phone = nil
- phone, err = m.ParsePhoneNumber(udata)
- if err != nil {
- return nil, err
- }
- customer.Phone = gocloak.StringP(phone.String())
- customer.Metafields = append(customer.Metafields,
- &generated.Metafield{
- Namespace: "customer",
- Key: "phone_region",
- Type: "single_line_text_field",
- Value: phone.PhoneRegion,
- },
- &generated.Metafield{
- Namespace: "customer",
- Key: "phone_verified",
- Type: "boolean",
- Value: fmt.Sprintf("%v", phone.Verified),
- })
- return &customer, nil
- }
- func (r *Resolver) admin(ctx context.Context) (*gocloak.JWT, error) {
- var (
- creds = r.conf.Admin
- err error
- )
- if time.Now().After(r._expiresAt) {
- r.mu.Lock()
- defer r.mu.Unlock()
- if r._token != nil {
- r._token, err = r.client.RefreshToken(ctx, r._token.RefreshToken, creds.ClientId, creds.ClientSecret, creds.Realm)
- }
- if r._token == nil || err != nil {
- r._token, err = r.client.LoginAdmin(ctx, creds.Login, creds.Password, creds.Realm)
- }
- if r._token == nil || err != nil {
- r._expiresAt = time.Time{}
- } else {
- r._expiresAt = time.Now().
- Add(time.Duration(r._token.ExpiresIn) * time.Second).
- Add(-time.Second * 5)
- }
- }
- return r._token, err
- }
- func (r *Resolver) saveSession(ctx context.Context, token *gocloak.JWT) error {
- return auth.SessionManager().PutToken(
- ctx,
- token.SessionState,
- token.RefreshToken,
- time.Duration(token.RefreshExpiresIn)*time.Second)
- }
- // Mutation returns generated.MutationResolver implementation.
- func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} }
- // Query returns generated.QueryResolver implementation.
- func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
- type mutationResolver struct{ *Resolver }
- type queryResolver struct{ *Resolver }
|