Skip to content

Commit

Permalink
add sign in with apple
Browse files Browse the repository at this point in the history
  • Loading branch information
iliyanm committed Jul 12, 2023
1 parent 54c7f19 commit e27403b
Show file tree
Hide file tree
Showing 14 changed files with 173 additions and 33 deletions.
2 changes: 2 additions & 0 deletions docs/docs/guide/services/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ service.DI().Authentication()

`FacebookService` # optional , when you need to support facebook login

`AppleService` # optional , when you need to support apple login

```go
func Authenticate(ormService *beeorm.Engine, uniqueValue string, password string, entity AuthProviderEntity) (accessToken string, refreshToken string, err error) {}
func VerifyAccessToken(ormService *beeorm.Engine, accessToken string, entity beeorm.Entity) error {}
Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
firebase.google.com/go v3.13.0+incompatible
github.com/99designs/gqlgen v0.17.16
github.com/AmirSoleimani/VoucherCodeGenerator v0.0.0-20201014193813-0206853dccb9
github.com/Timothylock/go-signin-with-apple v0.2.0
github.com/aws/aws-sdk-go v1.38.39
github.com/aymerick/raymond v2.0.2+incompatible
github.com/bojanz/currency v1.0.2
Expand Down Expand Up @@ -84,6 +85,7 @@ require (
github.com/gobwas/ws v1.1.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.4.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
Expand Down Expand Up @@ -115,6 +117,8 @@ require (
github.com/shamaton/msgpack v1.2.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/tideland/golib v4.24.2+incompatible // indirect
github.com/tideland/gorest v2.15.5+incompatible // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/vimeo/go-util v1.2.0 // indirect
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb0
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/Timothylock/go-signin-with-apple v0.2.0 h1:vP/4aKkp1eX2bGizNanWR79yixL3hWnwnxhvqr1hufk=
github.com/Timothylock/go-signin-with-apple v0.2.0/go.mod h1:EwflTtMTDy1azEwzpQHgAKgSDzogPwdp0eHK8znMiOY=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
Expand Down Expand Up @@ -252,6 +254,8 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
Expand Down Expand Up @@ -555,6 +559,10 @@ github.com/stripe/stripe-go/v72 v72.91.0 h1:aZBz1IeXs2G3MAVmE4bYSBcVYcHrLWGyTz2C
github.com/stripe/stripe-go/v72 v72.91.0/go.mod h1:QwqJQtduHubZht9mek5sds9CtQcKFdsykV9ZepRWwo0=
github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE=
github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM=
github.com/tideland/golib v4.24.2+incompatible h1:QYMkA3Sr1G8UWJTDsptrLOUwB6N89ETlVBnK5lZXKAw=
github.com/tideland/golib v4.24.2+incompatible/go.mod h1:HPHOmtCdCHUQiGAVZnlOH5eNTAEmM7R9oCFXdgvkB+Y=
github.com/tideland/gorest v2.15.5+incompatible h1:R19qOZQaCzT0x7ZExRd3avyG39jNLFeq2/HYetctYYo=
github.com/tideland/gorest v2.15.5+incompatible/go.mod h1:iCPpLOEr3tuQa96whkwiNTyYK4u6PTpWRxf5wGAvYLQ=
github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 h1:5u+EJUQiosu3JFX0XS0qTf5FznsMOzTjGqavBGuCbo0=
github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2/go.mod h1:4kyMkleCiLkgY6z8gK5BkI01ChBtxR0ro3I1ZDcGM3w=
github.com/ttacon/libphonenumber v1.2.1 h1:fzOfY5zUADkCkbIafAed11gL1sW+bJ26p6zWLBMElR4=
Expand Down
1 change: 1 addition & 0 deletions service/component/authentication/authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const (

SocialLoginGoogle = "google"
SocialLoginFacebook = "facebook"
SocialLoginApple = "apple"
)

type AuthenticatableEntity interface {
Expand Down
87 changes: 87 additions & 0 deletions service/component/social/apple.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package social

import (
"context"
"fmt"

"github.com/Timothylock/go-signin-with-apple/apple"
)

type Apple struct {
isAndroid bool
teamID string
clientID string
androidClientID string
keyID string
privateKey string
}

func NewAppleSocial(
teamID string,
clientID string,
androidClientID string,
keyID string,
privateKey string,
) IUserData {
return &Apple{
teamID: teamID,
clientID: clientID,
androidClientID: androidClientID,
keyID: keyID,
privateKey: privateKey,
}
}

func (a *Apple) GetUserData(ctx context.Context, token string) (*UserData, error) {
if a.isAndroid && a.androidClientID == "" {
return nil, fmt.Errorf("you must set androidClientID")
}
if !a.isAndroid && a.clientID == "" {

Check failure on line 39 in service/component/social/apple.go

View workflow job for this annotation

GitHub Actions / Quality & Security checks

if statements should only be cuddled with assignments (wsl)

Check failure on line 39 in service/component/social/apple.go

View workflow job for this annotation

GitHub Actions / Quality & Security checks

if statements should only be cuddled with assignments (wsl)
return nil, fmt.Errorf("you must set clientID")
}

clientID := a.clientID
if a.isAndroid {
clientID = a.androidClientID
}

secret, err := apple.GenerateClientSecret(a.privateKey, a.teamID, clientID, a.keyID)
if err != nil {
return nil, err
}

client := apple.New()

req := apple.AppValidationTokenRequest{
ClientID: clientID,
ClientSecret: secret,
Code: token,
}

var resp apple.ValidationResponse

err = client.VerifyAppToken(ctx, req, &resp)
if err != nil {
return nil, err
}

if resp.Error != "" {
return nil, fmt.Errorf(resp.Error)
}

claim, err := apple.GetClaims(resp.IDToken)
if err != nil {
return nil, err
}

emailClaim, ok := (*claim)["email"]
if !ok {
return nil, fmt.Errorf("apple returned claims with 'email' missling")
}

return &UserData{Email: emailClaim.(string)}, nil
}

func (a *Apple) SetIsAndroid(isAndroid bool) {
a.isAndroid = isAndroid
}
5 changes: 4 additions & 1 deletion service/component/social/facebook.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package social

import (
"context"
"encoding/json"
"io"
"net/http"
Expand All @@ -17,7 +18,7 @@ type facebookUserData struct {
type Facebook struct {
}

func (p *Facebook) GetUserData(token string) (*UserData, error) {
func (p *Facebook) GetUserData(_ context.Context, token string) (*UserData, error) {
resp, err := http.Get("https://graph.facebook.com/me?access_token=" +
url.QueryEscape(token))
defer func(body io.ReadCloser) {
Expand Down Expand Up @@ -48,3 +49,5 @@ func (p *Facebook) GetUserData(token string) (*UserData, error) {
Email: facebookUser.Email,
}, nil
}

func (p *Facebook) SetIsAndroid(_ bool) {}
5 changes: 4 additions & 1 deletion service/component/social/google.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package social

import (
"context"
"encoding/json"
"errors"
"io"
Expand All @@ -18,7 +19,7 @@ type googleUserData struct {
type Google struct {
}

func (p *Google) GetUserData(token string) (*UserData, error) {
func (p *Google) GetUserData(_ context.Context, token string) (*UserData, error) {
resp, err := http.Get("https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=" +
url.QueryEscape(token))
defer func(body io.ReadCloser) {
Expand Down Expand Up @@ -53,3 +54,5 @@ func (p *Google) GetUserData(token string) (*UserData, error) {
Email: googleUser.Email,
}, nil
}

func (p *Google) SetIsAndroid(_ bool) {}
15 changes: 0 additions & 15 deletions service/component/social/mocks/facebook.go

This file was deleted.

15 changes: 0 additions & 15 deletions service/component/social/mocks/google.go

This file was deleted.

18 changes: 18 additions & 0 deletions service/component/social/mocks/social.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package mocks

import (
"context"

"github.com/coretrix/hitrix/service/component/social"

Check failure on line 6 in service/component/social/mocks/social.go

View workflow job for this annotation

GitHub Actions / Quality & Security checks

File is not `gci`-ed with --skip-generated -s standard,default,prefix(github.com/coretrix/hitrix) (gci)

Check failure on line 6 in service/component/social/mocks/social.go

View workflow job for this annotation

GitHub Actions / Quality & Security checks

File is not `gci`-ed with --skip-generated -s standard,default,prefix(github.com/coretrix/hitrix) (gci)
"github.com/stretchr/testify/mock"

Check failure on line 7 in service/component/social/mocks/social.go

View workflow job for this annotation

GitHub Actions / Quality & Security checks

File is not `gci`-ed with --skip-generated -s standard,default,prefix(github.com/coretrix/hitrix) (gci)

Check failure on line 7 in service/component/social/mocks/social.go

View workflow job for this annotation

GitHub Actions / Quality & Security checks

File is not `gci`-ed with --skip-generated -s standard,default,prefix(github.com/coretrix/hitrix) (gci)
)

type Social struct {
mock.Mock
}

func (m *Social) GetUserData(_ context.Context, token string) (*social.UserData, error) {
return m.Called(token).Get(0).(*social.UserData), m.Called(token).Error(1)
}

func (m *Social) SetIsAndroid(_ bool) {}
5 changes: 4 additions & 1 deletion service/component/social/social.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package social

import "context"

type IUserData interface {
GetUserData(token string) (*UserData, error)
GetUserData(ctx context.Context, token string) (*UserData, error)
SetIsAndroid(isAndroid bool)
}

type UserData struct {
Expand Down
10 changes: 10 additions & 0 deletions service/registry/authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@ func ServiceProviderAuthentication() *service.DefinitionGlobal {
socialServiceMapping[authentication.SocialLoginFacebook] = googleService.(social.IUserData)
}

supportSocialLoginApple, ok := configService.Bool("authentication.support_social_login_apple")
if ok && supportSocialLoginApple {
appleService, err := ctn.SafeGet(service.AppleService)
if err != nil {
panic("apple service not loaded")
}

socialServiceMapping[authentication.SocialLoginApple] = appleService.(social.IUserData)
}

if appService.RedisPools == nil || appService.RedisPools.Persistent == "" {
panic("redis persistent needs to be set")
}
Expand Down
30 changes: 30 additions & 0 deletions service/registry/social.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/sarulabs/di"

"github.com/coretrix/hitrix/service"
"github.com/coretrix/hitrix/service/component/config"
"github.com/coretrix/hitrix/service/component/social"
)

Expand All @@ -24,3 +25,32 @@ func ServiceProviderFacebookSocial() *service.DefinitionGlobal {
},
}
}

func ServiceProviderAppleSocial() *service.DefinitionGlobal {
return &service.DefinitionGlobal{
Name: service.AppleService,
Build: func(ctn di.Container) (interface{}, error) {
configService := ctn.Get(service.ConfigService).(config.IConfig)

var clientID, androidClientID string

teamID := configService.MustString("authentication.apple.team_id")
if clientIDInner, ok := configService.String("authentication.apple.client_id"); ok {
clientID = clientIDInner
}
if androidClientIDInner, ok := configService.String("authentication.apple.android_client_id"); ok {
androidClientID = androidClientIDInner
}
keyID := configService.MustString("authentication.apple.key_id")
privateKey := configService.MustString("authentication.apple.private_key")

return social.NewAppleSocial(
teamID,
clientID,
androidClientID,
keyID,
privateKey,
), nil
},
}
}
1 change: 1 addition & 0 deletions service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ const (
MailService = "mail"
GoogleService = "google"
FacebookService = "facebook"
AppleService = "apple"
CrudService = "crud"
UUIDService = "uuid"
OTPService = "otp"
Expand Down

0 comments on commit e27403b

Please sign in to comment.