-
Notifications
You must be signed in to change notification settings - Fork 1
/
validator.go
executable file
·135 lines (113 loc) · 3.89 KB
/
validator.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package apple
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strings"
"time"
"github.com/tideland/gorest/jwt"
)
const (
// ValidationURL is the endpoint for verifying tokens
ValidationURL string = "https://appleid.apple.com/auth/token"
// ContentType is the one expected by Apple
ContentType string = "application/x-www-form-urlencoded"
// UserAgent is required by Apple or the request will fail
UserAgent string = "go-sign-with-apple"
// AcceptHeader is the content that we are willing to accept
AcceptHeader string = "application/json"
)
// ValidationClient is an interface to call the validation API
type ValidationClient interface {
VerifyWebToken(ctx context.Context, reqBody WebValidationTokenRequest, result interface{}) error
VerifyAppToken(ctx context.Context, reqBody AppValidationTokenRequest, result interface{}) error
VerifyRefreshToken(ctx context.Context, reqBody ValidationRefreshRequest, result interface{}) error
}
// Client implements ValidationClient
type Client struct {
validationURL string
client *http.Client
}
// NewClient creates a Client object
func NewClient() *Client {
client := &Client{
validationURL: ValidationURL,
client: &http.Client{
Timeout: 5 * time.Second,
},
}
return client
}
// NewWithURL creates a Client object with a custom URL provided
func NewWithURL(url string) *Client {
client := &Client{
validationURL: url,
client: &http.Client{
Timeout: 5 * time.Second,
},
}
return client
}
// VerifyWebToken sends the WebValidationTokenRequest and gets validation result
func (c *Client) VerifyWebToken(ctx context.Context, reqBody WebValidationTokenRequest, result interface{}) error {
data := url.Values{}
data.Set("client_id", reqBody.ClientID)
data.Set("client_secret", reqBody.ClientSecret)
data.Set("code", reqBody.Code)
data.Set("redirect_uri", reqBody.RedirectURI)
data.Set("grant_type", "authorization_code")
return doRequest(c.client, &result, c.validationURL, data)
}
// VerifyAppToken sends the AppValidationTokenRequest and gets validation result
func (c *Client) VerifyAppToken(ctx context.Context, reqBody AppValidationTokenRequest, result interface{}) error {
data := url.Values{}
data.Set("client_id", reqBody.ClientID)
data.Set("client_secret", reqBody.ClientSecret)
data.Set("code", reqBody.Code)
data.Set("grant_type", "authorization_code")
return doRequest(c.client, &result, c.validationURL, data)
}
// VerifyRefreshToken sends the WebValidationTokenRequest and gets validation result
func (c *Client) VerifyRefreshToken(ctx context.Context, reqBody ValidationRefreshRequest, result interface{}) error {
data := url.Values{}
data.Set("client_id", reqBody.ClientID)
data.Set("client_secret", reqBody.ClientSecret)
data.Set("refresh_token", reqBody.RefreshToken)
data.Set("grant_type", "refresh_token")
return doRequest(c.client, &result, c.validationURL, data)
}
// GetUniqueID decodes the id_token response and returns the unique subject ID to identify the user
func GetUniqueID(idToken string) (string, error) {
j, err := jwt.Decode(idToken)
if err != nil {
return "", err
}
return fmt.Sprintf("%v", j.Claims()["sub"]), nil
}
// GetClaims decodes the id_token response and returns the JWT claims to identify the user
func GetClaims(idToken string) (*jwt.Claims, error) {
j, err := jwt.Decode(idToken)
if err != nil {
return nil, err
}
claim := j.Claims()
return &claim, nil
}
//doRequest http request
func doRequest(client *http.Client, result interface{}, url string, data url.Values) error {
req, err := http.NewRequest("POST", url, strings.NewReader(data.Encode()))
if err != nil {
return err
}
req.Header.Add("content-type", ContentType)
req.Header.Add("accept", AcceptHeader)
req.Header.Add("user-agent", UserAgent) // apple requires a user agent
res, err := client.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
return json.NewDecoder(res.Body).Decode(result)
}