@@ -3,7 +3,6 @@ package oauth2
33import (
44 "context"
55 "crypto/sha256"
6- "crypto/x509"
76 "encoding/base64"
87 "encoding/json"
98 "fmt"
@@ -62,7 +61,7 @@ type ClientConfig struct {
6261 Scopes []string
6362 AuthMethod string
6463 PKCE bool
65- NoPKCE bool
64+ PAR bool
6665 Insecure bool
6766 ResponseType []string
6867 ResponseMode string
@@ -81,12 +80,8 @@ type ClientConfig struct {
8180 TLSRootCA string
8281}
8382
84- func RequestAuthorization (addr string , cconfig ClientConfig , sconfig ServerConfig ) (r Request , codeVerifier string , err error ) {
85- if r .URL , err = url .Parse (sconfig .AuthorizationEndpoint ); err != nil {
86- return r , "" , errors .Wrapf (err , "failed to parse authorization endpoint" )
87- }
88-
89- values := url.Values {
83+ func NewAuthorizationRequest (addr string , cconfig ClientConfig ) (values url.Values , codeVerifier string , err error ) {
84+ values = url.Values {
9085 "client_id" : {cconfig .ClientID },
9186 "redirect_uri" : {"http://" + addr + "/callback" },
9287 "state" : {shortuuid .New ()},
@@ -111,7 +106,7 @@ func RequestAuthorization(addr string, cconfig ClientConfig, sconfig ServerConfi
111106 hash := sha256 .New ()
112107
113108 if _ , err = hash .Write ([]byte (codeVerifier )); err != nil {
114- return r , "" , err
109+ return values , "" , err
115110 }
116111
117112 codeChallenge := CodeChallengeEncoder .EncodeToString (hash .Sum ([]byte {}))
@@ -120,12 +115,108 @@ func RequestAuthorization(addr string, cconfig ClientConfig, sconfig ServerConfi
120115 values .Set ("code_challenge_method" , "S256" )
121116 }
122117
118+ return values , codeVerifier , nil
119+ }
120+
121+ func RequestAuthorization (addr string , cconfig ClientConfig , sconfig ServerConfig ) (r Request , codeVerifier string , err error ) {
122+ var values url.Values
123+
124+ if r .URL , err = url .Parse (sconfig .AuthorizationEndpoint ); err != nil {
125+ return r , "" , errors .Wrapf (err , "failed to parse authorization endpoint" )
126+ }
127+
128+ if values , codeVerifier , err = NewAuthorizationRequest (addr , cconfig ); err != nil {
129+ return r , "" , errors .Wrapf (err , "failed to create authorization request" )
130+ }
131+
123132 r .URL .RawQuery = values .Encode ()
124133 r .Method = http .MethodGet
125134
126135 return r , codeVerifier , nil
127136}
128137
138+ type PARResponse struct {
139+ RequestURI string `json:"request_uri"`
140+ ExpiresIn int64 `json:"expires_in"`
141+ }
142+
143+ func RequestPAR (
144+ ctx context.Context ,
145+ addr string ,
146+ cconfig ClientConfig ,
147+ sconfig ServerConfig ,
148+ hc * http.Client ,
149+ ) (parRequest Request , parResponse PARResponse , authorizeRequest Request , codeVerifier string , err error ) {
150+ var (
151+ req * http.Request
152+ resp * http.Response
153+ endpoint string
154+ )
155+
156+ // push authorization request to /par
157+ if parRequest .Form , codeVerifier , err = NewAuthorizationRequest (addr , cconfig ); err != nil {
158+ return parRequest , parResponse , authorizeRequest , "" , errors .Wrapf (err , "failed to create authorization request" )
159+ }
160+
161+ if endpoint , err = parRequest .AuthenticateClient (
162+ sconfig .PushedAuthorizationRequestEndpoint ,
163+ sconfig .MTLsEndpointAliases .PushedAuthorizationRequestEndpoint ,
164+ cconfig ,
165+ sconfig ,
166+ hc ,
167+ ); err != nil {
168+ return parRequest , parResponse , authorizeRequest , "" , errors .Wrapf (err , "failed to create client authentication request" )
169+ }
170+
171+ if req , err = http .NewRequestWithContext (
172+ ctx ,
173+ http .MethodPost ,
174+ endpoint ,
175+ strings .NewReader (parRequest .Form .Encode ()),
176+ ); err != nil {
177+ return parRequest , parResponse , authorizeRequest , codeVerifier , err
178+ }
179+
180+ if cconfig .AuthMethod == ClientSecretBasicAuthMethod {
181+ req .SetBasicAuth (cconfig .ClientID , cconfig .ClientSecret )
182+ }
183+
184+ req .Header .Add ("Content-Type" , "application/x-www-form-urlencoded" )
185+
186+ parRequest .Method = req .Method
187+ parRequest .Headers = req .Header
188+ parRequest .URL = req .URL
189+
190+ if resp , err = hc .Do (req ); err != nil {
191+ return parRequest , parResponse , authorizeRequest , codeVerifier , err
192+ }
193+
194+ defer resp .Body .Close ()
195+
196+ if resp .StatusCode != http .StatusCreated {
197+ return parRequest , parResponse , authorizeRequest , codeVerifier , ParseError (resp )
198+ }
199+
200+ if err = json .NewDecoder (resp .Body ).Decode (& parResponse ); err != nil {
201+ return parRequest , parResponse , authorizeRequest , codeVerifier , fmt .Errorf ("failed to parse token response: %w" , err )
202+ }
203+
204+ // build request to /authorize
205+ if authorizeRequest .URL , err = url .Parse (sconfig .AuthorizationEndpoint ); err != nil {
206+ return parRequest , parResponse , authorizeRequest , codeVerifier , errors .Wrapf (err , "failed to create authorization request" )
207+ }
208+
209+ values := url.Values {
210+ "client_id" : {cconfig .ClientID },
211+ "request_uri" : {parResponse .RequestURI },
212+ }
213+
214+ authorizeRequest .URL .RawQuery = values .Encode ()
215+ authorizeRequest .Method = http .MethodGet
216+
217+ return parRequest , parResponse , authorizeRequest , codeVerifier , nil
218+ }
219+
129220func WaitForCallback (clientConfig ClientConfig , serverConfig ServerConfig , addr string , hc * http.Client ) (request Request , err error ) {
130221 var (
131222 srv = http.Server {Addr : addr }
@@ -273,7 +364,7 @@ func RequestToken(
273364 req * http.Request
274365 resp * http.Response
275366 params RequestTokenParams
276- endpoint = sconfig . TokenEndpoint
367+ endpoint string
277368 body []byte
278369 )
279370
@@ -319,45 +410,14 @@ func RequestToken(
319410 request .Form .Set ("device_code" , params .DeviceCode )
320411 }
321412
322- switch cconfig .AuthMethod {
323- case NoneAuthMethod :
324- request .Form .Set ("client_id" , cconfig .ClientID )
325- case ClientSecretPostAuthMethod :
326- request .Form .Set ("client_id" , cconfig .ClientID )
327- request .Form .Set ("client_secret" , cconfig .ClientSecret )
328- case ClientSecretJwtAuthMethod :
329- var clientAssertion string
330-
331- if clientAssertion , request .Key , err = SignJWT (
332- ClientAssertionClaims (sconfig , cconfig ),
333- SecretSigner ([]byte (cconfig .ClientSecret )),
334- ); err != nil {
335- return request , response , err
336- }
337-
338- request .Form .Set ("client_assertion_type" , JwtBearerClientAssertion )
339- request .Form .Set ("client_assertion" , clientAssertion )
340- case PrivateKeyJwtAuthMethod :
341- var clientAssertion string
342-
343- if clientAssertion , request .Key , err = SignJWT (
344- ClientAssertionClaims (sconfig , cconfig ),
345- JWKSigner (cconfig , hc ),
346- ); err != nil {
347- return request , response , err
348- }
349-
350- request .Form .Set ("client_assertion_type" , JwtBearerClientAssertion )
351- request .Form .Set ("client_assertion" , clientAssertion )
352- case TLSClientAuthMethod , SelfSignedTLSAuthMethod :
353- endpoint = sconfig .MTLsEndpointAliases .TokenEndpoint
354- request .Form .Set ("client_id" , cconfig .ClientID )
355-
356- if tr , ok := hc .Transport .(* http.Transport ); ok {
357- if len (tr .TLSClientConfig .Certificates ) > 0 {
358- request .Cert , _ = x509 .ParseCertificate (tr .TLSClientConfig .Certificates [0 ].Certificate [0 ])
359- }
360- }
413+ if endpoint , err = request .AuthenticateClient (
414+ sconfig .TokenEndpoint ,
415+ sconfig .MTLsEndpointAliases .TokenEndpoint ,
416+ cconfig ,
417+ sconfig ,
418+ hc ,
419+ ); err != nil {
420+ return request , response , err
361421 }
362422
363423 if params .RedirectURL != "" {
0 commit comments