Skip to content

Commit de0fcb9

Browse files
committed
sign in with apple
1 parent d813d26 commit de0fcb9

File tree

10 files changed

+169
-47
lines changed

10 files changed

+169
-47
lines changed

api/v1/auth/auth.go

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ func oauth2URL(c *gin.Context) {
3939
oauth2Client := c.MustGet("oauth2Client").(model.Authentication)
4040

4141
referer := c.Request.URL.Query().Get("referer")
42+
connection := c.Request.URL.Query().Get("connection")
43+
44+
providers := []string{}
4245

4346
var err error
4447
var state, clientId, codeUrl, audience, redirect_uri string
@@ -90,15 +93,23 @@ func oauth2URL(c *gin.Context) {
9093
if referer != "" {
9194
codeUrl = codeUrl + "&referer=" + referer
9295
}
96+
if connection != "" {
97+
codeUrl = codeUrl + "&connection=" + connection
98+
}
99+
}
100+
101+
if os.Getenv("PROVIDERS") != "" {
102+
providers = strings.Split(os.Getenv("PROVIDERS"), ",")
93103
}
94104

95105
data := &model.Auth{
96-
Oauth2: true,
97-
ClientId: clientId,
98-
State: state,
99-
CodeUrl: codeUrl,
100-
Audience: audience,
101-
Redirect: redirect_uri,
106+
Oauth2: true,
107+
ClientId: clientId,
108+
State: state,
109+
CodeUrl: codeUrl,
110+
Audience: audience,
111+
Redirect: redirect_uri,
112+
Providers: providers,
102113
}
103114

104115
log.Infof("model.Auth = %v", data)
@@ -146,8 +157,8 @@ func oauth2Exchange(c *gin.Context) {
146157
var oauth2Token *oauth2.Token
147158
var err error
148159

149-
if loginVals.Redirect != "com.nettica.agent://callback/agent" {
150-
oauth2Token, err = oauth2Client.Exchange(loginVals.Code)
160+
if loginVals.Redirect != "com.nettica.agent://callback/agent" || loginVals.Connection == "apple" {
161+
oauth2Token, err = oauth2Client.Exchange(loginVals)
151162
} else {
152163
oauth2Token, err = oauth2Client.Exchange2(loginVals.Code)
153164
}
@@ -201,7 +212,7 @@ func token(c *gin.Context) {
201212
var err error
202213

203214
if loginVals.Redirect != "com.nettica.agent://callback/agent" {
204-
oauth2Token, err = oauth2Client.Exchange(loginVals.Code)
215+
oauth2Token, err = oauth2Client.Exchange(loginVals)
205216
} else {
206217
oauth2Token, err = oauth2Client.Exchange2(loginVals.Code)
207218
}

auth/basic/basic.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@ func (o *Oauth2Basic) CodeUrl2(state string) string {
4646
}
4747

4848
// Exchange exchange code for Oauth2 token
49-
func (o *Oauth2Basic) Exchange(code string) (*oauth2.Token, error) {
49+
func (o *Oauth2Basic) Exchange(auth model.Auth) (*oauth2.Token, error) {
5050

5151
// code contains the username and password base64 encoded
5252
// base64 decode the string
53-
userpass, err := base64.StdEncoding.DecodeString(code)
53+
userpass, err := base64.StdEncoding.DecodeString(auth.Code)
5454
if err != nil {
5555
return nil, err
5656
}
@@ -89,7 +89,7 @@ func (o *Oauth2Basic) Exchange(code string) (*oauth2.Token, error) {
8989
}
9090

9191
func (o *Oauth2Basic) Exchange2(code string) (*oauth2.Token, error) {
92-
token, err := o.Exchange(code)
92+
token, err := o.Exchange(model.Auth{Code: code})
9393
return token, err
9494
}
9595

auth/fake/fake.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func (o *Fake) CodeUrl2(state string) string {
2626
}
2727

2828
// Exchange exchange code for Oauth2 token
29-
func (o *Fake) Exchange(code string) (*oauth2.Token, error) {
29+
func (o *Fake) Exchange(auth model.Auth) (*oauth2.Token, error) {
3030
rand, err := util.GenerateRandomString(32)
3131
if err != nil {
3232
return nil, err
@@ -40,7 +40,7 @@ func (o *Fake) Exchange(code string) (*oauth2.Token, error) {
4040
}, nil
4141
}
4242
func (o *Fake) Exchange2(code string) (*oauth2.Token, error) {
43-
token, err := o.Exchange(code)
43+
token, err := o.Exchange(model.Auth{Code: code})
4444
return token, err
4545
}
4646

auth/github/github.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ func (o *Github) CodeUrl2(state string) string {
4444
}
4545

4646
// Exchange exchange code for Oauth2 token
47-
func (o *Github) Exchange(code string) (*oauth2.Token, error) {
48-
oauth2Token, err := oauth2Config.Exchange(context.TODO(), code)
47+
func (o *Github) Exchange(auth model.Auth) (*oauth2.Token, error) {
48+
oauth2Token, err := oauth2Config.Exchange(context.TODO(), auth.Code)
4949
if err != nil {
5050
return nil, err
5151
}
@@ -54,7 +54,7 @@ func (o *Github) Exchange(code string) (*oauth2.Token, error) {
5454
}
5555

5656
func (o *Github) Exchange2(code string) (*oauth2.Token, error) {
57-
token, err := o.Exchange(code)
57+
token, err := o.Exchange(model.Auth{Code: code})
5858
return token, err
5959
}
6060

auth/google/google.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ func (o *OAuth2Google) CodeUrl2(state string) string {
6262
}
6363

6464
// Exchange exchange code for Oauth2 token
65-
func (o *OAuth2Google) Exchange(code string) (*oauth2.Token, error) {
66-
oauth2Token, err := oauth2Config.Exchange(context.TODO(), code)
65+
func (o *OAuth2Google) Exchange(auth model.Auth) (*oauth2.Token, error) {
66+
oauth2Token, err := oauth2Config.Exchange(context.TODO(), auth.Code)
6767
if err != nil {
6868
return nil, err
6969
}
@@ -72,7 +72,7 @@ func (o *OAuth2Google) Exchange(code string) (*oauth2.Token, error) {
7272
}
7373

7474
func (o *OAuth2Google) Exchange2(code string) (*oauth2.Token, error) {
75-
token, err := o.Exchange(code)
75+
token, err := o.Exchange(model.Auth{Code: code})
7676
return token, err
7777
}
7878

auth/microsoft/microsoft.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ func (o *Oauth2Msft) CodeUrl2(state string) string {
6363
}
6464

6565
// Exchange exchange code for Oauth2 token
66-
func (o *Oauth2Msft) Exchange(code string) (*oauth2.Token, error) {
67-
oauth2Token, err := oauth2Config.Exchange(context.TODO(), code)
66+
func (o *Oauth2Msft) Exchange(auth model.Auth) (*oauth2.Token, error) {
67+
oauth2Token, err := oauth2Config.Exchange(context.TODO(), auth.Code)
6868
if err != nil {
6969
return nil, err
7070
}
@@ -73,7 +73,7 @@ func (o *Oauth2Msft) Exchange(code string) (*oauth2.Token, error) {
7373
}
7474

7575
func (o *Oauth2Msft) Exchange2(code string) (*oauth2.Token, error) {
76-
token, err := o.Exchange(code)
76+
token, err := o.Exchange(model.Auth{Code: code})
7777
return token, err
7878
}
7979

auth/microsoft2/microsoft2.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,10 @@ func (o *Oauth2Microsoft) CodeUrl2(state string) string {
103103
}
104104

105105
// Exchange exchange code for Oauth2 token
106-
func (o *Oauth2Microsoft) Exchange(code string) (*oauth2.Token, error) {
106+
func (o *Oauth2Microsoft) Exchange(auth model.Auth) (*oauth2.Token, error) {
107107
// oauth2Token, err := oauth2Config.Exchange(context.TODO(), code)
108108

109-
authResult, err := clientApp.AcquireTokenByAuthCode(context.Background(), code, oauth2Config.RedirectURL, oauth2Config.Scopes)
109+
authResult, err := clientApp.AcquireTokenByAuthCode(context.Background(), auth.Code, oauth2Config.RedirectURL, oauth2Config.Scopes)
110110
if err != nil {
111111
return nil, err
112112
}

auth/oauth2oidc/oauth2oidc.go

Lines changed: 121 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,80 @@ func (o *Oauth2idc) CodeUrl2(state string) string {
121121
}
122122

123123
// Exchange exchange code for Oauth2 token
124-
func (o *Oauth2idc) Exchange(code string) (*oauth2.Token, error) {
124+
func (o *Oauth2idc) Exchange(auth model.Auth) (*oauth2.Token, error) {
125+
126+
if auth.Connection == "apple" {
127+
// Make a http request using the agent configuration information
128+
client_id := os.Getenv("OAUTH2_AGENT_CLIENT_ID")
129+
client_secret := os.Getenv("OAUTH2_AGENT_CLIENT_SECRET")
130+
redirect_url := os.Getenv("OAUTH2_AGENT_REDIRECT_URL")
131+
audience := os.Getenv("OAUTH2_AGENT_AUDIENCE")
132+
133+
provider := os.Getenv("OAUTH2_AGENT_PROVIDER")
134+
135+
// make an http post to the oauth2 token endpoint
136+
// with the code and other required parameters
137+
// to get the access token
138+
// and other information
139+
140+
httpClient := &http.Client{
141+
Transport: &CustomTransport{
142+
Transport: http.DefaultTransport,
143+
UserAgent: "nettica-admin/2.0",
144+
},
145+
}
146+
rsp, err := httpClient.PostForm(provider+"oauth/token/", url.Values{
147+
"grant_type": {"urn:ietf:params:oauth:grant-type:token-exchange"},
148+
"client_id": {client_id},
149+
"client_secret": {client_secret},
150+
"redirect_uri": {redirect_url},
151+
"audience": {audience},
152+
"subject_token": {auth.Code},
153+
"subject_token_type": {"http://auth0.com/oauth/token-type/apple-authz-code"},
154+
})
155+
if err != nil {
156+
log.Info(err)
157+
return nil, err
158+
}
159+
defer rsp.Body.Close()
125160

126-
oauth2Token, err := oauth2Config.Exchange(ctx, code)
127-
if err != nil {
128-
return nil, err
161+
body, err := io.ReadAll(rsp.Body)
162+
if err != nil {
163+
log.Info(err)
164+
return nil, err
165+
}
166+
log.Infof("body: %s", body)
167+
168+
// read the response body and serialize it into a TokenResponse struct
169+
var tokenResponse TokenResponse
170+
171+
// decode the body bytes into the struct
172+
173+
err = json.Unmarshal(body, &tokenResponse)
174+
if err != nil {
175+
log.Info(err)
176+
return nil, err
177+
}
178+
179+
oauth2Token := &oauth2.Token{
180+
AccessToken: tokenResponse.AccessToken,
181+
Expiry: time.Now().Add(time.Duration(tokenResponse.ExpiresIn) * time.Second),
182+
TokenType: tokenResponse.TokenType,
183+
}
184+
oauth2Token = oauth2Token.WithExtra(map[string]interface{}{ // Add the ID token to the extra parameters
185+
"id_token": auth.IdToken})
186+
187+
return oauth2Token, nil
188+
189+
} else {
190+
oauth2Token, err := oauth2Config.Exchange(ctx, auth.Code)
191+
if err != nil {
192+
return nil, err
193+
}
194+
return oauth2Token, nil
129195
}
130196

131-
return oauth2Token, nil
197+
return nil, nil
132198
}
133199
func (o *Oauth2idc) Exchange2(code string) (*oauth2.Token, error) {
134200

@@ -199,6 +265,29 @@ func (o *Oauth2idc) Exchange2(code string) (*oauth2.Token, error) {
199265
return oauth2Token, nil
200266
}
201267

268+
func verifyAppleIDToken(ctx context.Context, tokenString string) (*oidc.IDToken, error) {
269+
// Apple's OIDC discovery URL
270+
provider, err := oidc.NewProvider(ctx, "https://appleid.apple.com")
271+
if err != nil {
272+
return nil, fmt.Errorf("failed to get Apple OIDC provider: %v", err)
273+
}
274+
275+
// Set up an ID Token verifier using the provider and your client ID
276+
verifier := provider.Verifier(&oidc.Config{ClientID: "com.nettica.agent"})
277+
if verifier == nil {
278+
return nil, fmt.Errorf("failed to create verifier")
279+
}
280+
281+
// Verify and parse ID Token
282+
idToken, err := verifier.Verify(ctx, tokenString)
283+
if err != nil {
284+
return nil, fmt.Errorf("ID Token verification failed: %v", err)
285+
}
286+
287+
// Successfully verified ID Token
288+
return idToken, nil
289+
}
290+
202291
// UserInfo get token user
203292
func (o *Oauth2idc) UserInfo(oauth2Token *oauth2.Token) (*model.User, error) {
204293
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
@@ -208,7 +297,10 @@ func (o *Oauth2idc) UserInfo(oauth2Token *oauth2.Token) (*model.User, error) {
208297

209298
verified := false
210299
var idToken *oidc.IDToken
300+
var userInfo *oidc.UserInfo
211301
var err error
302+
// ID Token payload is just JSON
303+
var claims map[string]interface{}
212304

213305
for _, verifier := range oidcIDTokenVerifier {
214306
idToken, err = verifier.Verify(ctx, rawIDToken)
@@ -218,6 +310,21 @@ func (o *Oauth2idc) UserInfo(oauth2Token *oauth2.Token) (*model.User, error) {
218310
}
219311
}
220312

313+
if !verified {
314+
idToken, err = verifyAppleIDToken(ctx, rawIDToken)
315+
if err == nil {
316+
verified = true
317+
}
318+
319+
// get the user info from the id token
320+
idToken.Claims(&claims)
321+
userInfo = &oidc.UserInfo{
322+
Subject: "apple|" + idToken.Subject,
323+
Email: claims["email"].(string),
324+
}
325+
// add the claims to the user info
326+
}
327+
221328
if !verified || err != nil {
222329
return nil, err
223330
}
@@ -227,15 +334,16 @@ func (o *Oauth2idc) UserInfo(oauth2Token *oauth2.Token) (*model.User, error) {
227334
return cacheUser.(*model.User), nil
228335
}
229336

230-
userInfo, err := oidcProvider.UserInfo(ctx, oauth2.StaticTokenSource(oauth2Token))
231-
if err != nil {
232-
return nil, err
233-
}
337+
if userInfo == nil {
338+
userInfo, err = oidcProvider.UserInfo(ctx, oauth2.StaticTokenSource(oauth2Token))
339+
if err != nil {
340+
return nil, err
341+
}
342+
343+
if err := userInfo.Claims(&claims); err != nil {
344+
return nil, fmt.Errorf("failed to get id token claims: %s", err)
345+
}
234346

235-
// ID Token payload is just JSON
236-
var claims map[string]interface{}
237-
if err := userInfo.Claims(&claims); err != nil {
238-
return nil, fmt.Errorf("failed to get id token claims: %s", err)
239347
}
240348

241349
// get some infos about user

model/auth.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@ import "golang.org/x/oauth2"
44

55
// Auth structure
66
type Auth struct {
7-
Oauth2 bool `json:"oauth2"`
8-
ClientId string `json:"clientId"`
9-
Code string `json:"code"`
10-
State string `json:"state"`
11-
CodeUrl string `json:"codeUrl"`
12-
Redirect string `json:"redirect_uri"`
13-
Audience string `json:"audience"`
7+
Oauth2 bool `json:"oauth2"`
8+
ClientId string `json:"clientId"`
9+
Code string `json:"code"`
10+
State string `json:"state"`
11+
CodeUrl string `json:"codeUrl"`
12+
Redirect string `json:"redirect_uri"`
13+
Audience string `json:"audience"`
14+
Connection string `json:"connection"`
15+
IdToken string `json:"id_token"`
16+
Providers []string `json:"providers"`
1417
}
1518

1619
type OAuth2Token struct {
@@ -26,7 +29,7 @@ type Authentication interface {
2629
Setup() error
2730
CodeUrl(state string) string
2831
CodeUrl2(state string) string
29-
Exchange(code string) (*oauth2.Token, error)
32+
Exchange(auth Auth) (*oauth2.Token, error)
3033
Exchange2(code string) (*oauth2.Token, error)
3134
UserInfo(oauth2Token *oauth2.Token) (*User, error)
3235
}

util/util.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ var (
2525

2626
func GetCleanAuthToken(c *gin.Context) string {
2727
token := c.Request.Header.Get(AuthTokenHeaderName)
28-
if len(token) > 0 && token[:7] == "Bearer " {
28+
if len(token) > 0 && strings.HasPrefix(token, "Bearer ") {
2929
token = token[7:]
3030
token = strings.Trim(token, "\"")
3131
}

0 commit comments

Comments
 (0)