-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathauthorize_endpoint.go
More file actions
203 lines (174 loc) · 5.68 KB
/
authorize_endpoint.go
File metadata and controls
203 lines (174 loc) · 5.68 KB
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package oauthx
import (
"bytes"
"context"
"net/url"
"strings"
"github.com/vdbulcke/assert"
)
type AuthorizationRequest struct {
Url string
ReqCtx *OAuthContext
PARResp *PushedAuthorizationRequestResponse
}
// DoAuthorizationRequest make authorization code flow request
//
// Example:
//
// // NewBaseAuthzRequest create a new base AuthZRequest with
// // resonable default:
// // - nonce
// // - state
// // - response_type=code
// // - pkce S256
// req := oauthx.NewBaseAuthzRequest()
// // add client specific options
// req.AddOpts(
// oauthx.ClientIdOpt("my_client_id"),
// oauthx.RedirectUriOpt("https://my.domain.com/callback"),
// oauthx.ScopeOpt("openid", "profile", "email"),
// )
// // let's make the authorization request via PAR.
// // Some options are not related to specific oauth parameter
// // but how the DoAuthorizationRequest() function should behave
// req.AddOpts(
// // sends authorization request options via
// // pushed authorization endpoint and
// // only use client_id and request_uri for
// // redirect to the authorization_endpoint
// oauthx.WithPushedAuthorizationRequest(),
// )
// // now let's make the authorzation request
// // (if using options shuch as WithPushedAuthorizationRequest()
// // this will make an acutual http request), then this will
// // generate the authorization url use to redirect the user to the
// // Authorization Server, a OAuthContext (containing relevant parameter
// // that may be required to make the associated token request: pkce code_verifier,
// // redirect_uri, client_id, etc).
// authRequest, err := client.DoAuthorizationRequest(ctx, req)
// if err != nil {
// // handle err
// return err
// }
// // authRequest.Url is the authorization request url
//
// implements rfc6749 authorization code flow, rfc9126, rfc9101
func (c *OAuthClient) DoAuthorizationRequest(ctx context.Context, authz *AuthZRequest) (*AuthorizationRequest, error) {
originalOpts := authz.opts
params, err := c.PlumbingGenerateAuthZRequestParam(authz)
if err != nil {
return nil, err
}
requestContext := authz.reqCtx
if !requestContext.WithRFC9126Par {
if requestContext.WithRFC9101Request && requestContext.WithStrictRequiredAuthorizationParams {
for _, opt := range originalOpts {
// seek on which oauth param the current option is
seekParam := url.Values{}
opt.SetValue(seekParam)
// RCF6749 (OAuth2 ) and OIDC standard
// requires mandatory parameter present
// RCF6749 Section 4.1.1
// - response_type
// - client_id
if seekParam.Has("response_type") || seekParam.Has("client_id") {
// add it again to authorization url param
opt.SetValue(params)
continue
}
//
// openid core spec:
// - scope (MUST includes 'openid')
// - redirect_uri
if seekParam.Has("scope") || seekParam.Has("redirect_uri") {
// add it again to authorization url param
opt.SetValue(params)
}
}
}
authUrl := c.PlumbingGenerateAuthorizationUrl(params)
resp := &AuthorizationRequest{
Url: authUrl,
ReqCtx: requestContext,
}
return resp, nil
}
req, err := c.PlumbingNewHttpPARRequest(params)
if err != nil {
return nil, err
}
parResp, err := c.PlumbingDoHttpPARRequest(ctx, req)
if err != nil {
return nil, err
}
// 4. Authorization Request
// The client uses the "request_uri" value returned by the authorization
// server to build an authorization request as defined in [RFC9101].
newAuthzReq := NewAuthZRequest(
// rfc9101
// request_uri
// REQUIRED unless "request" is specified. The absolute URI, as
// defined by RFC 3986 [RFC3986], that is the Request Object URI
// (Section 2.2) referencing the authorization request parameters
// stated in Section 4 of [RFC6749] (OAuth 2.0). If this parameter
// is present in the authorization request, "request" MUST NOT be
// present.
PlumbingRequestUriOpt(parResp.RequestUri),
// rfc9101
// client_id
// REQUIRED. OAuth 2.0 [RFC6749] "client_id". The value MUST match
// the "request" or "request_uri" Request Object's (Section 2.1)
// "client_id".
ClientIdOpt(c.ClientId),
)
newParams, err := c.PlumbingGenerateAuthZRequestParam(newAuthzReq)
if err != nil {
return nil, err
}
if requestContext.WithStrictRequiredAuthorizationParams {
for _, opt := range originalOpts {
// seek on which oauth param the current option is
seekParam := url.Values{}
opt.SetValue(seekParam)
// RCF6749 (OAuth2 ) and OIDC standard
// requires mandatory parameter present
// RCF6749 Section 4.1.1
// - response_type
// - client_id
if seekParam.Has("response_type") || seekParam.Has("client_id") {
// add it again to authorization url param
opt.SetValue(newParams)
continue
}
//
// openid core spec:
// - scope (MUST includes 'openid')
// - redirect_uri
if seekParam.Has("scope") || seekParam.Has("redirect_uri") {
// add it again to authorization url param
opt.SetValue(newParams)
}
}
}
authUrl := c.PlumbingGenerateAuthorizationUrl(newParams)
return &AuthorizationRequest{
Url: authUrl,
PARResp: parResp,
ReqCtx: requestContext,
}, nil
}
func (c *OAuthClient) PlumbingGenerateAuthorizationUrl(params url.Values) string {
assert.StrNotEmpty(c.wk.AuthorizationEndpoint, assert.Panic, "oauth-client: missing required 'authorization_endpoint'")
return PlumbingAddParamToEndpoint(c.wk.AuthorizationEndpoint, params)
}
func PlumbingAddParamToEndpoint(endpoint string, params url.Values) string {
var buf bytes.Buffer
buf.WriteString(endpoint)
if strings.Contains(endpoint, "?") {
buf.WriteByte('&')
} else {
buf.WriteByte('?')
}
buf.WriteString(params.Encode())
return buf.String()
}