/
handler_oidc_userinfo.go
135 lines (101 loc) · 3.96 KB
/
handler_oidc_userinfo.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 handlers
import (
"fmt"
"net/http"
"time"
"github.com/google/uuid"
"github.com/ory/fosite"
"github.com/ory/fosite/token/jwt"
"github.com/pkg/errors"
"github.com/authelia/authelia/v4/internal/middlewares"
"github.com/authelia/authelia/v4/internal/model"
"github.com/authelia/authelia/v4/internal/oidc"
)
// OpenIDConnectUserinfo handles GET/POST requests to the OpenID Connect 1.0 UserInfo endpoint.
//
// https://openid.net/specs/openid-connect-core-1_0.html#UserInfo
func OpenIDConnectUserinfo(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, req *http.Request) {
var (
tokenType fosite.TokenType
requester fosite.AccessRequester
client *oidc.Client
err error
)
oidcSession := oidc.NewSession()
if tokenType, requester, err = ctx.Providers.OpenIDConnect.Fosite.IntrospectToken(
req.Context(), fosite.AccessTokenFromRequest(req), fosite.AccessToken, oidcSession); err != nil {
rfc := fosite.ErrorToRFC6749Error(err)
ctx.Logger.Errorf("UserInfo Request failed with error: %+v", rfc)
if rfc.StatusCode() == http.StatusUnauthorized {
rw.Header().Set("WWW-Authenticate", fmt.Sprintf(`Bearer error="%s",error_description="%s"`, rfc.ErrorField, rfc.GetDescription()))
}
ctx.Providers.OpenIDConnect.WriteError(rw, req, err)
return
}
clientID := requester.GetClient().GetID()
if tokenType != fosite.AccessToken {
ctx.Logger.Errorf("UserInfo Request with id '%s' on client with id '%s' failed with error: bearer authorization failed as the token is not an access_token", requester.GetID(), client.GetID())
errStr := "Only access tokens are allowed in the authorization header."
rw.Header().Set("WWW-Authenticate", fmt.Sprintf(`Bearer error="invalid_token",error_description="%s"`, errStr))
ctx.Providers.OpenIDConnect.WriteErrorCode(rw, req, http.StatusUnauthorized, errors.New(errStr))
return
}
if client, err = ctx.Providers.OpenIDConnect.Store.GetFullClient(clientID); err != nil {
ctx.Providers.OpenIDConnect.WriteError(rw, req, errors.WithStack(fosite.ErrServerError.WithHint("Unable to assert type of client")))
return
}
claims := requester.GetSession().(*model.OpenIDSession).IDTokenClaims().ToMap()
delete(claims, "jti")
delete(claims, "sid")
delete(claims, "at_hash")
delete(claims, "c_hash")
delete(claims, "exp")
delete(claims, "nonce")
audience, ok := claims["aud"].([]string)
if !ok || len(audience) == 0 {
audience = []string{client.GetID()}
} else {
found := false
for _, aud := range audience {
if aud == clientID {
found = true
break
}
}
if found {
audience = append(audience, clientID)
}
}
claims["aud"] = audience
var (
keyID, token string
)
ctx.Logger.Tracef("UserInfo Response with id '%s' on client with id '%s' is being sent with the following claims: %+v", requester.GetID(), clientID, claims)
switch client.UserinfoSigningAlgorithm {
case "RS256":
var jti uuid.UUID
if jti, err = uuid.NewRandom(); err != nil {
ctx.Providers.OpenIDConnect.WriteError(rw, req, fosite.ErrServerError.WithHintf("Could not generate JTI."))
return
}
claims["jti"] = jti.String()
claims["iat"] = time.Now().Unix()
if keyID, err = ctx.Providers.OpenIDConnect.KeyManager.Strategy().GetPublicKeyID(req.Context()); err != nil {
ctx.Providers.OpenIDConnect.WriteError(rw, req, fosite.ErrServerError.WithHintf("Could not find the active JWK."))
return
}
headers := &jwt.Headers{
Extra: map[string]interface{}{"kid": keyID},
}
if token, _, err = ctx.Providers.OpenIDConnect.KeyManager.Strategy().Generate(req.Context(), claims, headers); err != nil {
ctx.Providers.OpenIDConnect.WriteError(rw, req, err)
return
}
rw.Header().Set("Content-Type", "application/jwt")
_, _ = rw.Write([]byte(token))
case "none", "":
ctx.Providers.OpenIDConnect.Write(rw, req, claims)
default:
ctx.Providers.OpenIDConnect.WriteError(rw, req, errors.WithStack(fosite.ErrServerError.WithHintf("Unsupported UserInfo signing algorithm '%s'.", client.UserinfoSigningAlgorithm)))
}
}