This repository was archived by the owner on Oct 23, 2024. It is now read-only.
forked from arrikto/oidc-authservice
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathoidc.go
132 lines (110 loc) · 3.43 KB
/
oidc.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
package main
import (
"context"
"encoding/json"
"io/ioutil"
"net/http"
"strconv"
"github.com/coreos/go-oidc"
"github.com/pkg/errors"
"golang.org/x/oauth2"
)
// UserInfo represents the OpenID Connect userinfo claims.
type UserInfo struct {
Subject string `json:"sub"`
Profile string `json:"profile"`
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
RawClaims []byte
}
// Claims unmarshals the raw JSON object claims into the provided object.
func (u *UserInfo) Claims(v interface{}) error {
if u.RawClaims == nil {
return errors.New("oidc: claims not set")
}
return json.Unmarshal(u.RawClaims, v)
}
// ParseUserInfo unmarshals the response of the UserInfo endpoint
// and enforces boolean value for the EmailVerified claim.
func ParseUserInfo(body []byte) (*UserInfo, error){
raw := struct {
Subject string `json:"sub"`
Profile string `json:"profile"`
Email string `json:"email"`
EmailVerified interface{} `json:"email_verified"`
RawClaims []byte
}{}
err := json.Unmarshal(body, &raw)
if err != nil {
return nil, errors.Errorf("oidc: fail to decode userinfo: %v", err)
}
userInfo := &UserInfo{
Subject: raw.Subject,
Profile: raw.Profile,
Email: raw.Email,
}
switch ParsedEmailVerified := raw.EmailVerified.(type) {
case bool:
userInfo.EmailVerified = ParsedEmailVerified
case string:
boolValue, err := strconv.ParseBool(ParsedEmailVerified)
if err != nil {
return nil, errors.Errorf("oidc: failed to decode the email_verified field of userinfo: %v", err)
}
userInfo.EmailVerified = boolValue
case nil:
userInfo.EmailVerified = false
default:
return nil, errors.Errorf("oidc: unsupported type for the email_verified field")
}
userInfo.RawClaims = body
return userInfo, nil
}
// GetUserInfo uses the token source to query the provider's user info endpoint.
// We reimplement UserInfo [1] instead of using the go-oidc's library UserInfo, in
// order to include HTTP response information in case of an error during
// contacting the UserInfo endpoint.
//
// [1]: https://github.com/coreos/go-oidc/blob/v2.1.0/oidc.go#L180
func GetUserInfo(ctx context.Context, provider *oidc.Provider, tokenSource oauth2.TokenSource) (*UserInfo, error) {
discoveryClaims := &struct {
UserInfoURL string `json:"userinfo_endpoint"`
}{}
if err := provider.Claims(discoveryClaims); err != nil {
return nil, errors.Errorf("Error unmarshalling OIDC discovery document claims: %v", err)
}
userInfoURL := discoveryClaims.UserInfoURL
if userInfoURL == "" {
return nil, errors.New("oidc: user info endpoint is not supported by this provider")
}
req, err := http.NewRequest("GET", userInfoURL, nil)
if err != nil {
return nil, errors.Errorf("oidc: create GET request: %v", err)
}
token, err := tokenSource.Token()
if err != nil {
return nil, errors.Errorf("oidc: get access token: %v", err)
}
token.SetAuthHeader(req)
resp, err := doRequest(ctx, req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, &requestError{
Response: resp,
Body: body,
Err: errors.Errorf("oidc: Calling UserInfo endpoint failed. body: %s", body),
}
}
userInfo, err := ParseUserInfo(body)
if err != nil {
return nil, errors.Errorf("oidc: failed to parse userInfo body: %v", err)
}
return userInfo, nil
}