github.com/openshift-online/ocm-sdk-go@v0.1.473/authentication/transport_wrapper_test.go (about)

     1  //go:build linux
     2  // +build linux
     3  
     4  /*
     5  Copyright (c) 2019 Red Hat, Inc.
     6  
     7  Licensed under the Apache License, Version 2.0 (the "License");
     8  you may not use this file except in compliance with the License.
     9  You may obtain a copy of the License at
    10  
    11    http://www.apache.org/licenses/LICENSE-2.0
    12  
    13  Unless required by applicable law or agreed to in writing, software
    14  distributed under the License is distributed on an "AS IS" BASIS,
    15  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16  See the License for the specific language governing permissions and
    17  limitations under the License.
    18  */
    19  
    20  // This file contains tests for the methods that request tokens.
    21  
    22  package authentication
    23  
    24  import (
    25  	"context"
    26  	"encoding/base64"
    27  	"errors"
    28  	"fmt"
    29  	"net/http"
    30  	"os"
    31  	"strings"
    32  	"time"
    33  
    34  	"github.com/golang-jwt/jwt/v4"
    35  	"github.com/google/uuid"
    36  
    37  	. "github.com/onsi/ginkgo/v2/dsl/core"             // nolint
    38  	. "github.com/onsi/gomega"                         // nolint
    39  	. "github.com/onsi/gomega/ghttp"                   // nolint
    40  	. "github.com/openshift-online/ocm-sdk-go/testing" // nolint
    41  )
    42  
    43  var _ = Describe("Tokens", func() {
    44  	// Context used by the tests:
    45  	var ctx context.Context
    46  
    47  	// Server used during the tests:
    48  	var server *Server
    49  
    50  	// Name of the temporary file containing the CA for the server:
    51  	var ca string
    52  
    53  	BeforeEach(func() {
    54  		// Create the context:
    55  		ctx = context.Background()
    56  
    57  		// Create the servers:
    58  		server, ca = MakeTCPTLSServer()
    59  	})
    60  
    61  	AfterEach(func() {
    62  		// Stop the servers:
    63  		server.Close()
    64  
    65  		// Remove the temporary CA files:
    66  		err := os.Remove(ca)
    67  		Expect(err).ToNot(HaveOccurred())
    68  	})
    69  
    70  	It("Can be created tokens that don't have the `typ` claim", func() {
    71  		// Generate the tokens:
    72  		accessToken := MakeTokenObject(jwt.MapClaims{
    73  			"typ": nil,
    74  			"exp": time.Now().Add(5 * time.Minute).Unix(),
    75  		}).Raw
    76  		refreshToken := MakeTokenObject(jwt.MapClaims{
    77  			"typ": nil,
    78  			"exp": time.Now().Add(10 * time.Hour).Unix(),
    79  		}).Raw
    80  
    81  		// Create the wrapper:
    82  		wrapper, err := NewTransportWrapper().
    83  			Logger(logger).
    84  			TokenURL(server.URL()).
    85  			TrustedCA(ca).
    86  			Tokens(accessToken, refreshToken).
    87  			Build(ctx)
    88  		Expect(err).ToNot(HaveOccurred())
    89  		defer func() {
    90  			err = wrapper.Close()
    91  			Expect(err).ToNot(HaveOccurred())
    92  		}()
    93  
    94  		// Get the tokens:
    95  		returnedAccess, returnedRefresh, err := wrapper.Tokens(ctx)
    96  		Expect(err).ToNot(HaveOccurred())
    97  		Expect(returnedAccess).To(Equal(accessToken))
    98  		Expect(returnedRefresh).To(Equal(refreshToken))
    99  	})
   100  
   101  	Describe("Refresh grant", func() {
   102  		It("Returns the access token generated by the server", func() {
   103  			// Generate the tokens:
   104  			accessToken := MakeTokenString("Bearer", 5*time.Minute)
   105  			refreshToken := MakeTokenString("Refresh", 10*time.Hour)
   106  
   107  			// Configure the server:
   108  			server.AppendHandlers(
   109  				CombineHandlers(
   110  					VerifyRefreshGrant(refreshToken),
   111  					RespondWithAccessAndRefreshTokens(accessToken, refreshToken),
   112  				),
   113  			)
   114  
   115  			// Create the wrapper:
   116  			wrapper, err := NewTransportWrapper().
   117  				Logger(logger).
   118  				TokenURL(server.URL()).
   119  				TrustedCA(ca).
   120  				Tokens(refreshToken).
   121  				Build(ctx)
   122  			Expect(err).ToNot(HaveOccurred())
   123  			defer func() {
   124  				err = wrapper.Close()
   125  				Expect(err).ToNot(HaveOccurred())
   126  			}()
   127  
   128  			// Get the tokens:
   129  			returnedAccess, returnedRefresh, err := wrapper.Tokens(ctx)
   130  			Expect(err).ToNot(HaveOccurred())
   131  			Expect(returnedAccess).To(Equal(accessToken))
   132  			Expect(returnedRefresh).To(Equal(refreshToken))
   133  		})
   134  
   135  		It("Accepts tokens without the `typ` claim", func() {
   136  			// Generate the tokens:
   137  			accessToken := MakeTokenObject(jwt.MapClaims{
   138  				"typ": nil,
   139  				"exp": time.Now().Add(5 * time.Minute).Unix(),
   140  			}).Raw
   141  			refreshToken := MakeTokenObject(jwt.MapClaims{
   142  				"typ": nil,
   143  				"exp": time.Now().Add(10 * time.Hour).Unix(),
   144  			}).Raw
   145  
   146  			// Configure the server:
   147  			server.AppendHandlers(
   148  				CombineHandlers(
   149  					VerifyRefreshGrant(refreshToken),
   150  					RespondWithAccessAndRefreshTokens(accessToken, refreshToken),
   151  				),
   152  			)
   153  
   154  			// Create the wrapper:
   155  			wrapper, err := NewTransportWrapper().
   156  				Logger(logger).
   157  				TokenURL(server.URL()).
   158  				TrustedCA(ca).
   159  				Tokens(accessToken, refreshToken).
   160  				Build(ctx)
   161  			Expect(err).ToNot(HaveOccurred())
   162  			defer func() {
   163  				err = wrapper.Close()
   164  				Expect(err).ToNot(HaveOccurred())
   165  			}()
   166  
   167  			// Get the tokens:
   168  			returnedAccess, returnedRefresh, err := wrapper.Tokens(ctx)
   169  			Expect(err).ToNot(HaveOccurred())
   170  			Expect(returnedAccess).To(Equal(accessToken))
   171  			Expect(returnedRefresh).To(Equal(refreshToken))
   172  		})
   173  
   174  		It("Sends the token request the first time only", func() {
   175  			// Generate the tokens:
   176  			accessToken := MakeTokenString("Bearer", 5*time.Minute)
   177  			refreshToken := MakeTokenString("Refresh", 10*time.Hour)
   178  
   179  			// Configure the server:
   180  			server.AppendHandlers(
   181  				CombineHandlers(
   182  					VerifyRefreshGrant(refreshToken),
   183  					RespondWithAccessAndRefreshTokens(accessToken, refreshToken),
   184  				),
   185  			)
   186  
   187  			// Create the wrapper:
   188  			wrapper, err := NewTransportWrapper().
   189  				Logger(logger).
   190  				TokenURL(server.URL()).
   191  				TrustedCA(ca).
   192  				Tokens(refreshToken).
   193  				Build(ctx)
   194  			Expect(err).ToNot(HaveOccurred())
   195  			defer func() {
   196  				err = wrapper.Close()
   197  				Expect(err).ToNot(HaveOccurred())
   198  			}()
   199  
   200  			// Get the tokens the first time:
   201  			firstAccess, firstRefresh, err := wrapper.Tokens(ctx)
   202  			Expect(err).ToNot(HaveOccurred())
   203  
   204  			// Get the tones the second time:
   205  			secondAccess, secondRefresh, err := wrapper.Tokens(ctx)
   206  			Expect(err).ToNot(HaveOccurred())
   207  			Expect(firstAccess).To(Equal(secondAccess))
   208  			Expect(firstRefresh).To(Equal(secondRefresh))
   209  		})
   210  
   211  		It("Refreshes the access token request if it is expired", func() {
   212  			// Generate the tokens:
   213  			expiredAccess := MakeTokenString("Bearer", -5*time.Minute)
   214  			validAccess := MakeTokenString("Bearer", 5*time.Minute)
   215  			refreshToken := MakeTokenString("Refresh", 10*time.Hour)
   216  
   217  			// Configure the server:
   218  			server.AppendHandlers(
   219  				CombineHandlers(
   220  					VerifyRefreshGrant(refreshToken),
   221  					RespondWithAccessAndRefreshTokens(validAccess, refreshToken),
   222  				),
   223  			)
   224  
   225  			// Create the wrapper:
   226  			wrapper, err := NewTransportWrapper().
   227  				Logger(logger).
   228  				TokenURL(server.URL()).
   229  				TrustedCA(ca).
   230  				Tokens(expiredAccess, refreshToken).
   231  				Build(ctx)
   232  			Expect(err).ToNot(HaveOccurred())
   233  			defer func() {
   234  				err = wrapper.Close()
   235  				Expect(err).ToNot(HaveOccurred())
   236  			}()
   237  
   238  			// Get the tokens:
   239  			returnedAccess, _, err := wrapper.Tokens(ctx)
   240  			Expect(err).ToNot(HaveOccurred())
   241  			Expect(returnedAccess).To(Equal(validAccess))
   242  		})
   243  
   244  		It("Uses opaque refresh token to refresh expired access token", func() {
   245  			// Generate the tokens:
   246  			expiredAccess := MakeTokenString("Bearer", -5*time.Minute)
   247  			validAccess := MakeTokenString("Bearer", 5*time.Minute)
   248  			refreshToken := "my_refresh_token"
   249  
   250  			// Configure the server:
   251  			server.AppendHandlers(
   252  				CombineHandlers(
   253  					VerifyRefreshGrant(refreshToken),
   254  					RespondWithAccessAndRefreshTokens(validAccess, refreshToken),
   255  				),
   256  			)
   257  
   258  			// Create the wrapper:
   259  			wrapper, err := NewTransportWrapper().
   260  				Logger(logger).
   261  				TokenURL(server.URL()).
   262  				TrustedCA(ca).
   263  				Tokens(expiredAccess, refreshToken).
   264  				Build(ctx)
   265  			Expect(err).ToNot(HaveOccurred())
   266  			defer func() {
   267  				err = wrapper.Close()
   268  				Expect(err).ToNot(HaveOccurred())
   269  			}()
   270  
   271  			// Get the tokens:
   272  			returnedAccess, _, err := wrapper.Tokens(ctx)
   273  			Expect(err).ToNot(HaveOccurred())
   274  			Expect(returnedAccess).To(Equal(validAccess))
   275  		})
   276  
   277  		It("Refreshes the access token if it expires in less than one minute", func() {
   278  			// Generate the tokens:
   279  			firstAccess := MakeTokenString("Bearer", 50*time.Second)
   280  			secondAccess := MakeTokenString("Bearer", 5*time.Minute)
   281  			refreshToken := MakeTokenString("Refresh", 10*time.Hour)
   282  
   283  			// Configure the server:
   284  			server.AppendHandlers(
   285  				CombineHandlers(
   286  					VerifyRefreshGrant(refreshToken),
   287  					RespondWithAccessAndRefreshTokens(secondAccess, refreshToken),
   288  				),
   289  			)
   290  
   291  			// Create the wrapper:
   292  			wrapper, err := NewTransportWrapper().
   293  				Logger(logger).
   294  				TokenURL(server.URL()).
   295  				TrustedCA(ca).
   296  				Tokens(firstAccess, refreshToken).
   297  				Build(ctx)
   298  			Expect(err).ToNot(HaveOccurred())
   299  			defer func() {
   300  				err = wrapper.Close()
   301  				Expect(err).ToNot(HaveOccurred())
   302  			}()
   303  
   304  			// Get the tokens:
   305  			returnedAccess, _, err := wrapper.Tokens(ctx)
   306  			Expect(err).ToNot(HaveOccurred())
   307  			Expect(returnedAccess).To(Equal(secondAccess))
   308  		})
   309  
   310  		It("Refreshes the access token if it expires in less than specified expiry period", func() {
   311  			// Ask for a token valid for at least 10 minutes
   312  			expiresIn := 10 * time.Minute
   313  
   314  			// Generate the tokens:
   315  			firstAccess := MakeTokenString("Bearer", 9*time.Minute)
   316  			secondAccess := MakeTokenString("Bearer", 20*time.Minute)
   317  			refreshToken := MakeTokenString("Refresh", 10*time.Hour)
   318  
   319  			// Configure the server:
   320  			server.AppendHandlers(
   321  				CombineHandlers(
   322  					VerifyRefreshGrant(refreshToken),
   323  					RespondWithAccessAndRefreshTokens(secondAccess, refreshToken),
   324  				),
   325  			)
   326  
   327  			// Create the wrapper:
   328  			wrapper, err := NewTransportWrapper().
   329  				Logger(logger).
   330  				TokenURL(server.URL()).
   331  				TrustedCA(ca).
   332  				Tokens(firstAccess, refreshToken).
   333  				Build(ctx)
   334  			Expect(err).ToNot(HaveOccurred())
   335  			defer func() {
   336  				err = wrapper.Close()
   337  				Expect(err).ToNot(HaveOccurred())
   338  			}()
   339  
   340  			// Get the tokens:
   341  			returnedAccess, _, err := wrapper.Tokens(ctx, expiresIn)
   342  			Expect(err).ToNot(HaveOccurred())
   343  			Expect(returnedAccess).To(Equal(secondAccess))
   344  		})
   345  
   346  		It("Fails if the access token is expired and there is no refresh token", func() {
   347  			// Generate the tokens:
   348  			accessToken := MakeTokenString("Bearer", -5*time.Second)
   349  
   350  			// Create the wrapper:
   351  			wrapper, err := NewTransportWrapper().
   352  				Logger(logger).
   353  				TokenURL(server.URL()).
   354  				TrustedCA(ca).
   355  				Tokens(accessToken).
   356  				Build(ctx)
   357  			Expect(err).ToNot(HaveOccurred())
   358  			defer func() {
   359  				err = wrapper.Close()
   360  				Expect(err).ToNot(HaveOccurred())
   361  			}()
   362  
   363  			// Get the tokens:
   364  			_, _, err = wrapper.Tokens(ctx)
   365  			Expect(err).To(HaveOccurred())
   366  		})
   367  
   368  		It("Succeeds if access token expires soon and there is no refresh token", func() {
   369  			// Generate the tokens:
   370  			accessToken := MakeTokenString("Bearer", 10*time.Second)
   371  
   372  			// Create the wrapper:
   373  			wrapper, err := NewTransportWrapper().
   374  				Logger(logger).
   375  				TokenURL(server.URL()).
   376  				TrustedCA(ca).
   377  				Tokens(accessToken).
   378  				Build(ctx)
   379  			Expect(err).ToNot(HaveOccurred())
   380  			defer func() {
   381  				err = wrapper.Close()
   382  				Expect(err).ToNot(HaveOccurred())
   383  			}()
   384  
   385  			// Get the tokens:
   386  			_, _, err = wrapper.Tokens(ctx)
   387  			Expect(err).ToNot(HaveOccurred())
   388  		})
   389  
   390  		It("Fails if the refresh token is expired", func() {
   391  			// Generate the tokens:
   392  			refreshToken := MakeTokenString("Refresh", -5*time.Second)
   393  
   394  			// Create the wrapper:
   395  			wrapper, err := NewTransportWrapper().
   396  				Logger(logger).
   397  				TokenURL(server.URL()).
   398  				TrustedCA(ca).
   399  				Tokens(refreshToken).
   400  				Build(ctx)
   401  			Expect(err).ToNot(HaveOccurred())
   402  			defer func() {
   403  				err = wrapper.Close()
   404  				Expect(err).ToNot(HaveOccurred())
   405  			}()
   406  
   407  			// Get the tokens:
   408  			_, _, err = wrapper.Tokens(ctx)
   409  			Expect(err).To(HaveOccurred())
   410  		})
   411  
   412  		When("The server doesn't return JSON content type", func() {
   413  			It("Adds complete content to error message if it is short", func() {
   414  				// Generate the refresh token:
   415  				refreshToken := MakeTokenString("Refresh", 10*time.Hour)
   416  
   417  				// Configure the server:
   418  				for i := 0; i < 100; i++ { // there are going to be several retries
   419  					server.AppendHandlers(
   420  						RespondWith(
   421  							http.StatusServiceUnavailable,
   422  							`Service unavailable`,
   423  							http.Header{
   424  								"Content-Type": []string{
   425  									"text/plain",
   426  								},
   427  							},
   428  						),
   429  					)
   430  				}
   431  
   432  				// Create the wrapper:
   433  				wrapper, err := NewTransportWrapper().
   434  					Logger(logger).
   435  					TokenURL(server.URL()).
   436  					TrustedCA(ca).
   437  					Tokens(refreshToken).
   438  					Build(ctx)
   439  				Expect(err).ToNot(HaveOccurred())
   440  				defer func() {
   441  					err = wrapper.Close()
   442  					Expect(err).ToNot(HaveOccurred())
   443  				}()
   444  
   445  				// Try to get the access token:
   446  				ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
   447  				defer cancel()
   448  				_, _, err = wrapper.Tokens(ctx)
   449  				Expect(err).To(HaveOccurred())
   450  				message := err.Error()
   451  				Expect(message).To(ContainSubstring("text/plain"))
   452  				Expect(message).To(ContainSubstring("Service unavailable"))
   453  			})
   454  
   455  			It("Adds summary of content if it is too long", func() {
   456  				// Generate the refresh token:
   457  				refreshToken := MakeTokenString("Refresh", 10*time.Hour)
   458  
   459  				// Calculate a long message:
   460  				content := fmt.Sprintf("Ver%s long", strings.Repeat("y", 1000))
   461  
   462  				// Configure the server:
   463  				server.AppendHandlers(
   464  					RespondWith(
   465  						http.StatusBadRequest,
   466  						content,
   467  						http.Header{
   468  							"Content-Type": []string{
   469  								"text/plain",
   470  							},
   471  						},
   472  					),
   473  				)
   474  
   475  				// Create the wrapper:
   476  				wrapper, err := NewTransportWrapper().
   477  					Logger(logger).
   478  					TokenURL(server.URL()).
   479  					TrustedCA(ca).
   480  					Tokens(refreshToken).
   481  					Build(ctx)
   482  				Expect(err).ToNot(HaveOccurred())
   483  				defer func() {
   484  					err = wrapper.Close()
   485  					Expect(err).ToNot(HaveOccurred())
   486  				}()
   487  
   488  				// Try to get the access token:
   489  				_, _, err = wrapper.Tokens(ctx)
   490  				Expect(err).To(HaveOccurred())
   491  				message := err.Error()
   492  				Expect(message).To(ContainSubstring("text/plain"))
   493  				Expect(message).To(ContainSubstring("Veryyyyyy"))
   494  				Expect(message).To(ContainSubstring("..."))
   495  			})
   496  		})
   497  
   498  		It("Honors cookies", func() {
   499  			// Generate the tokens:
   500  			expiredAccess := MakeTokenString("Bearer", -5*time.Minute)
   501  			validAccess := MakeTokenString("Bearer", 5*time.Minute)
   502  			refreshToken := MakeTokenString("Refresh", 10*time.Hour)
   503  
   504  			// Configure the server:
   505  			server.AppendHandlers(
   506  				CombineHandlers(
   507  					RespondWithCookie("mycookie", "myvalue"),
   508  					RespondWithAccessAndRefreshTokens(expiredAccess, refreshToken),
   509  				),
   510  				CombineHandlers(
   511  					VerifyCookie("mycookie", "myvalue"),
   512  					RespondWithAccessAndRefreshTokens(validAccess, refreshToken),
   513  				),
   514  			)
   515  
   516  			// Create the wrapper:
   517  			wrapper, err := NewTransportWrapper().
   518  				Logger(logger).
   519  				TokenURL(server.URL()).
   520  				TrustedCA(ca).
   521  				Tokens(expiredAccess, refreshToken).
   522  				Build(ctx)
   523  			Expect(err).ToNot(HaveOccurred())
   524  			defer func() {
   525  				err = wrapper.Close()
   526  				Expect(err).ToNot(HaveOccurred())
   527  			}()
   528  
   529  			// Request the tokens the first time. This will return an expired access
   530  			// token and a valid refresh token.
   531  			_, _, err = wrapper.Tokens(ctx)
   532  			Expect(err).ToNot(HaveOccurred())
   533  
   534  			// Request the tokens a second time, therefore forcing a refresh grant which
   535  			// should honor the cookies returned in the first attempt:
   536  			_, _, err = wrapper.Tokens(ctx)
   537  			Expect(err).ToNot(HaveOccurred())
   538  		})
   539  	})
   540  
   541  	Describe("Password grant", func() {
   542  		It("Returns the access and refresh tokens generated by the server", func() {
   543  			// Generate the tokens:
   544  			accessToken := MakeTokenString("Bearer", 5*time.Minute)
   545  			refreshToken := MakeTokenString("Refresh", 10*time.Hour)
   546  
   547  			// Configure the server:
   548  			server.AppendHandlers(
   549  				CombineHandlers(
   550  					VerifyPasswordGrant("myuser", "mypassword"),
   551  					RespondWithAccessAndRefreshTokens(accessToken, refreshToken),
   552  				),
   553  			)
   554  
   555  			// Create the wrapper:
   556  			wrapper, err := NewTransportWrapper().
   557  				Logger(logger).
   558  				TokenURL(server.URL()).
   559  				TrustedCA(ca).
   560  				User("myuser", "mypassword").
   561  				Build(ctx)
   562  			Expect(err).ToNot(HaveOccurred())
   563  			defer func() {
   564  				err = wrapper.Close()
   565  				Expect(err).ToNot(HaveOccurred())
   566  			}()
   567  
   568  			// Get the tokens:
   569  			returnedAccess, returnedRefresh, err := wrapper.Tokens(ctx)
   570  			Expect(err).ToNot(HaveOccurred())
   571  			Expect(returnedAccess).To(Equal(accessToken))
   572  			Expect(returnedRefresh).To(Equal(refreshToken))
   573  		})
   574  
   575  		It("Accepts tokens without the `typ` claim", func() {
   576  			// Generate the tokens:
   577  			accessToken := MakeTokenObject(jwt.MapClaims{
   578  				"typ": nil,
   579  				"exp": time.Now().Add(5 * time.Minute).Unix(),
   580  			}).Raw
   581  			refreshToken := MakeTokenObject(jwt.MapClaims{
   582  				"typ": nil,
   583  				"exp": time.Now().Add(10 * time.Hour).Unix(),
   584  			}).Raw
   585  
   586  			// Configure the server:
   587  			server.AppendHandlers(
   588  				CombineHandlers(
   589  					VerifyPasswordGrant("myuser", "mypassword"),
   590  					RespondWithAccessAndRefreshTokens(accessToken, refreshToken),
   591  				),
   592  			)
   593  
   594  			// Create the wrapper:
   595  			wrapper, err := NewTransportWrapper().
   596  				Logger(logger).
   597  				TokenURL(server.URL()).
   598  				TrustedCA(ca).
   599  				User("myuser", "mypassword").
   600  				Build(ctx)
   601  			Expect(err).ToNot(HaveOccurred())
   602  			defer func() {
   603  				err = wrapper.Close()
   604  				Expect(err).ToNot(HaveOccurred())
   605  			}()
   606  
   607  			// Get the tokens:
   608  			returnedAccess, returnedRefresh, err := wrapper.Tokens(ctx)
   609  			Expect(err).ToNot(HaveOccurred())
   610  			Expect(returnedAccess).To(Equal(accessToken))
   611  			Expect(returnedRefresh).To(Equal(refreshToken))
   612  		})
   613  
   614  		It("Refreshes access token", func() {
   615  			// Generate the tokens:
   616  			expiredAccess := MakeTokenString("Bearer", -5*time.Second)
   617  			validAccess := MakeTokenString("Bearer", 5*time.Minute)
   618  			refreshToken := MakeTokenString("Refresh", 10*time.Hour)
   619  
   620  			// Configure the server:
   621  			server.AppendHandlers(
   622  				CombineHandlers(
   623  					VerifyPasswordGrant("myuser", "mypassword"),
   624  					RespondWithAccessAndRefreshTokens(expiredAccess, refreshToken),
   625  				),
   626  				CombineHandlers(
   627  					VerifyRefreshGrant(refreshToken),
   628  					RespondWithAccessAndRefreshTokens(validAccess, refreshToken),
   629  				),
   630  			)
   631  
   632  			// Create the wrapper:
   633  			wrapper, err := NewTransportWrapper().
   634  				Logger(logger).
   635  				TokenURL(server.URL()).
   636  				TrustedCA(ca).
   637  				User("myuser", "mypassword").
   638  				Build(ctx)
   639  			Expect(err).ToNot(HaveOccurred())
   640  			defer func() {
   641  				err = wrapper.Close()
   642  				Expect(err).ToNot(HaveOccurred())
   643  			}()
   644  
   645  			// Get the tokens the first time:
   646  			firstAccess, _, err := wrapper.Tokens(ctx)
   647  			Expect(err).ToNot(HaveOccurred())
   648  			Expect(firstAccess).To(Equal(expiredAccess))
   649  
   650  			// Get the tokens the second time:
   651  			secondAccess, _, err := wrapper.Tokens(ctx)
   652  			Expect(err).ToNot(HaveOccurred())
   653  			Expect(secondAccess).To(Equal(validAccess))
   654  		})
   655  
   656  		It("Requests a new refresh token when it expires", func() {
   657  			// Generate the tokens:
   658  			expiredAccess := MakeTokenString("Bearer", -5*time.Second)
   659  			expiredRefresh := MakeTokenString("Refresh", -15*time.Second)
   660  			validAccess := MakeTokenString("Bearer", 5*time.Minute)
   661  			validRefresh := MakeTokenString("Refresh", 10*time.Hour)
   662  
   663  			// Configure the server:
   664  			server.AppendHandlers(
   665  				CombineHandlers(
   666  					VerifyPasswordGrant("myuser", "mypassword"),
   667  					RespondWithAccessAndRefreshTokens(expiredAccess, expiredRefresh),
   668  				),
   669  				CombineHandlers(
   670  					VerifyPasswordGrant("myuser", "mypassword"),
   671  					RespondWithAccessAndRefreshTokens(validAccess, validRefresh),
   672  				),
   673  			)
   674  
   675  			// Create the wrapper:
   676  			wrapper, err := NewTransportWrapper().
   677  				Logger(logger).
   678  				TokenURL(server.URL()).
   679  				TrustedCA(ca).
   680  				User("myuser", "mypassword").
   681  				Build(ctx)
   682  			Expect(err).ToNot(HaveOccurred())
   683  			defer func() {
   684  				err = wrapper.Close()
   685  				Expect(err).ToNot(HaveOccurred())
   686  			}()
   687  
   688  			// Get the tokens the first time:
   689  			_, firstRefresh, err := wrapper.Tokens(ctx)
   690  			Expect(err).ToNot(HaveOccurred())
   691  			Expect(firstRefresh).To(Equal(expiredRefresh))
   692  
   693  			// Get the tokens the second time:
   694  			_, secondRefresh, err := wrapper.Tokens(ctx)
   695  			Expect(err).ToNot(HaveOccurred())
   696  			Expect(secondRefresh).To(Equal(validRefresh))
   697  		})
   698  
   699  		It("Requests a new refresh token when expires in less than ten seconds", func() {
   700  			// Generate the tokens:
   701  			expiredAccess := MakeTokenString("Bearer", -5*time.Second)
   702  			expiredRefresh := MakeTokenString("Refresh", 5*time.Second)
   703  			validAccess := MakeTokenString("Bearer", 5*time.Minute)
   704  			validRefresh := MakeTokenString("Refresh", 10*time.Hour)
   705  
   706  			// Configure the server:
   707  			server.AppendHandlers(
   708  				CombineHandlers(
   709  					VerifyPasswordGrant("myuser", "mypassword"),
   710  					RespondWithAccessAndRefreshTokens(expiredAccess, expiredRefresh),
   711  				),
   712  				CombineHandlers(
   713  					VerifyPasswordGrant("myuser", "mypassword"),
   714  					RespondWithAccessAndRefreshTokens(validAccess, validRefresh),
   715  				),
   716  			)
   717  
   718  			// Create the wrapper:
   719  			wrapper, err := NewTransportWrapper().
   720  				Logger(logger).
   721  				TokenURL(server.URL()).
   722  				TrustedCA(ca).
   723  				User("myuser", "mypassword").
   724  				Build(ctx)
   725  			Expect(err).ToNot(HaveOccurred())
   726  			defer func() {
   727  				err = wrapper.Close()
   728  				Expect(err).ToNot(HaveOccurred())
   729  			}()
   730  
   731  			// Get the tokens the first time:
   732  			_, firstRefresh, err := wrapper.Tokens(ctx)
   733  			Expect(err).ToNot(HaveOccurred())
   734  			Expect(firstRefresh).To(Equal(expiredRefresh))
   735  
   736  			// Get the tokens the second time:
   737  			_, secondRefresh, err := wrapper.Tokens(ctx)
   738  			Expect(err).ToNot(HaveOccurred())
   739  			Expect(secondRefresh).To(Equal(validRefresh))
   740  		})
   741  
   742  		It("Fails with wrong user name", func() {
   743  			// Configure the server:
   744  			server.AppendHandlers(
   745  				CombineHandlers(
   746  					VerifyPasswordGrant("baduser", "mypassword"),
   747  					RespondWithTokenError("bad_user", "Bad user"),
   748  				),
   749  			)
   750  
   751  			// Create the wrapper:
   752  			wrapper, err := NewTransportWrapper().
   753  				Logger(logger).
   754  				TokenURL(server.URL()).
   755  				TrustedCA(ca).
   756  				User("baduser", "mypassword").
   757  				Build(ctx)
   758  			Expect(err).ToNot(HaveOccurred())
   759  			defer func() {
   760  				err = wrapper.Close()
   761  				Expect(err).ToNot(HaveOccurred())
   762  			}()
   763  
   764  			// Get the tokens:
   765  			_, _, err = wrapper.Tokens(ctx)
   766  			Expect(err).To(HaveOccurred())
   767  		})
   768  
   769  		It("Fails with wrong password", func() {
   770  			// Configure the server:
   771  			server.AppendHandlers(
   772  				CombineHandlers(
   773  					VerifyPasswordGrant("myuser", "badpassword"),
   774  					RespondWithTokenError("bad_password", "Bad password"),
   775  				),
   776  			)
   777  
   778  			// Create the wrapper:
   779  			wrapper, err := NewTransportWrapper().
   780  				Logger(logger).
   781  				TokenURL(server.URL()).
   782  				TrustedCA(ca).
   783  				User("myuser", "badpassword").
   784  				Build(ctx)
   785  			Expect(err).ToNot(HaveOccurred())
   786  			defer func() {
   787  				err = wrapper.Close()
   788  				Expect(err).ToNot(HaveOccurred())
   789  			}()
   790  
   791  			// Get the tokens:
   792  			_, _, err = wrapper.Tokens(ctx)
   793  			Expect(err).To(HaveOccurred())
   794  		})
   795  
   796  		It("Honors cookies", func() {
   797  			// Generate the tokens:
   798  			expiredAccess := MakeTokenString("Bearer", -5*time.Minute)
   799  			validAccess := MakeTokenString("Bearer", 5*time.Minute)
   800  			refreshToken := MakeTokenString("Refresh", 10*time.Hour)
   801  
   802  			// Configure the server:
   803  			server.AppendHandlers(
   804  				CombineHandlers(
   805  					RespondWithCookie("mycookie", "myvalue"),
   806  					RespondWithAccessAndRefreshTokens(expiredAccess, refreshToken),
   807  				),
   808  				CombineHandlers(
   809  					VerifyCookie("mycookie", "myvalue"),
   810  					RespondWithAccessAndRefreshTokens(validAccess, refreshToken),
   811  				),
   812  			)
   813  
   814  			// Create the wrapper:
   815  			wrapper, err := NewTransportWrapper().
   816  				Logger(logger).
   817  				TokenURL(server.URL()).
   818  				TrustedCA(ca).
   819  				User("myuser", "mypassword").
   820  				Build(ctx)
   821  			Expect(err).ToNot(HaveOccurred())
   822  			defer func() {
   823  				err = wrapper.Close()
   824  				Expect(err).ToNot(HaveOccurred())
   825  			}()
   826  
   827  			// Request the tokens the first time. This will return an expired access
   828  			// token and a valid refresh token.
   829  			_, _, err = wrapper.Tokens(ctx)
   830  			Expect(err).ToNot(HaveOccurred())
   831  
   832  			// Request the tokens a second time, therefore forcing a refresh grant which
   833  			// should honor the cookies returned in the first attempt:
   834  			_, _, err = wrapper.Tokens(ctx)
   835  			Expect(err).ToNot(HaveOccurred())
   836  		})
   837  
   838  		It("Works if no refresh token is returned", func() {
   839  			// Generate the tokens:
   840  			accessToken := MakeTokenString("Bearer", 5*time.Minute)
   841  
   842  			// Configure the server:
   843  			server.AppendHandlers(
   844  				CombineHandlers(
   845  					VerifyPasswordGrant("myuser", "mypassword"),
   846  					RespondWithAccessToken(accessToken),
   847  				),
   848  			)
   849  
   850  			// Create the wrapper:
   851  			wrapper, err := NewTransportWrapper().
   852  				Logger(logger).
   853  				TokenURL(server.URL()).
   854  				TrustedCA(ca).
   855  				User("myuser", "mypassword").
   856  				Build(ctx)
   857  			Expect(err).ToNot(HaveOccurred())
   858  			defer func() {
   859  				err = wrapper.Close()
   860  				Expect(err).ToNot(HaveOccurred())
   861  			}()
   862  
   863  			// Get the tokens:
   864  			returnedAccess, _, err := wrapper.Tokens(ctx)
   865  			Expect(err).ToNot(HaveOccurred())
   866  			Expect(returnedAccess).To(Equal(accessToken))
   867  		})
   868  
   869  		It("Accepts lower case token type", func() {
   870  			// Generate the tokens:
   871  			accessToken := MakeTokenString("bearer", 5*time.Minute)
   872  
   873  			// Configure the server:
   874  			server.AppendHandlers(
   875  				CombineHandlers(
   876  					VerifyPasswordGrant("myuser", "mypassword"),
   877  					RespondWithAccessToken(accessToken),
   878  				),
   879  			)
   880  
   881  			// Create the wrapper:
   882  			wrapper, err := NewTransportWrapper().
   883  				Logger(logger).
   884  				TokenURL(server.URL()).
   885  				TrustedCA(ca).
   886  				User("myuser", "mypassword").
   887  				Build(ctx)
   888  			Expect(err).ToNot(HaveOccurred())
   889  			defer func() {
   890  				err = wrapper.Close()
   891  				Expect(err).ToNot(HaveOccurred())
   892  			}()
   893  
   894  			// Get the tokens:
   895  			returnedAccess, _, err := wrapper.Tokens(ctx)
   896  			Expect(err).ToNot(HaveOccurred())
   897  			Expect(returnedAccess).To(Equal(accessToken))
   898  		})
   899  
   900  		It("Accepts opaque refresh token", func() {
   901  			// Generate the tokens:
   902  			accessToken := MakeTokenString("bearer", 5*time.Minute)
   903  			refreshToken := "my_refresh_token"
   904  
   905  			// Configure the server:
   906  			server.AppendHandlers(
   907  				CombineHandlers(
   908  					VerifyPasswordGrant("myuser", "mypassword"),
   909  					RespondWithAccessAndRefreshTokens(accessToken, refreshToken),
   910  				),
   911  			)
   912  
   913  			// Create the wrapper:
   914  			wrapper, err := NewTransportWrapper().
   915  				Logger(logger).
   916  				TokenURL(server.URL()).
   917  				TrustedCA(ca).
   918  				User("myuser", "mypassword").
   919  				Build(ctx)
   920  			Expect(err).ToNot(HaveOccurred())
   921  			defer func() {
   922  				err = wrapper.Close()
   923  				Expect(err).ToNot(HaveOccurred())
   924  			}()
   925  
   926  			// Get the tokens:
   927  			returnedAccess, returnedRefresh, err := wrapper.Tokens(ctx)
   928  			Expect(err).ToNot(HaveOccurred())
   929  			Expect(returnedAccess).To(Equal(accessToken))
   930  			Expect(returnedRefresh).To(Equal(refreshToken))
   931  		})
   932  	})
   933  
   934  	When("Only the access token is provided", func() {
   935  		It("Returns the access token if it hasn't expired", func() {
   936  			// Generate the token:
   937  			accessToken := MakeTokenString("Bearer", 5*time.Minute)
   938  
   939  			// Create the wrapper:
   940  			wrapper, err := NewTransportWrapper().
   941  				Logger(logger).
   942  				TokenURL(server.URL()).
   943  				TrustedCA(ca).
   944  				Tokens(accessToken).
   945  				Build(ctx)
   946  			Expect(err).ToNot(HaveOccurred())
   947  			defer func() {
   948  				err = wrapper.Close()
   949  				Expect(err).ToNot(HaveOccurred())
   950  			}()
   951  
   952  			// Get the tokens:
   953  			returnedAccess, returnedRefresh, err := wrapper.Tokens(ctx)
   954  			Expect(err).ToNot(HaveOccurred())
   955  			Expect(returnedAccess).To(Equal(accessToken))
   956  			Expect(returnedRefresh).To(BeEmpty())
   957  		})
   958  
   959  		It("Returns an error if the access token has expired", func() {
   960  			// Generate the token:
   961  			accessToken := MakeTokenString("Bearer", -5*time.Minute)
   962  
   963  			// Create the wrapper:
   964  			wrapper, err := NewTransportWrapper().
   965  				Logger(logger).
   966  				TokenURL(server.URL()).
   967  				TrustedCA(ca).
   968  				Tokens(accessToken).
   969  				Build(ctx)
   970  			Expect(err).ToNot(HaveOccurred())
   971  			defer func() {
   972  				err = wrapper.Close()
   973  				Expect(err).ToNot(HaveOccurred())
   974  			}()
   975  
   976  			// Get the tokens:
   977  			returnedAccess, returnedRefresh, err := wrapper.Tokens(ctx)
   978  			Expect(err).To(HaveOccurred())
   979  			Expect(returnedAccess).To(BeEmpty())
   980  			Expect(returnedRefresh).To(BeEmpty())
   981  		})
   982  	})
   983  
   984  	Describe("Client credentials grant", func() {
   985  		It("Returns the access token generated by the server", func() {
   986  			// Generate the tokens:
   987  			accessToken := MakeTokenString("Bearer", 5*time.Minute)
   988  
   989  			// Configure the server:
   990  			server.AppendHandlers(
   991  				CombineHandlers(
   992  					VerifyClientCredentialsGrant("myclient", "mysecret"),
   993  					RespondWithAccessToken(accessToken),
   994  				),
   995  			)
   996  
   997  			// Create the wrapper:
   998  			wrapper, err := NewTransportWrapper().
   999  				Logger(logger).
  1000  				TokenURL(server.URL()).
  1001  				TrustedCA(ca).
  1002  				Client("myclient", "mysecret").
  1003  				Build(ctx)
  1004  			Expect(err).ToNot(HaveOccurred())
  1005  			defer func() {
  1006  				err = wrapper.Close()
  1007  				Expect(err).ToNot(HaveOccurred())
  1008  			}()
  1009  
  1010  			// Get the token:
  1011  			returnedAccess, _, err := wrapper.Tokens(ctx)
  1012  			Expect(err).ToNot(HaveOccurred())
  1013  			Expect(returnedAccess).To(Equal(accessToken))
  1014  		})
  1015  
  1016  		It("Accepts token without the `typ` claim", func() {
  1017  			// Generate the tokens:
  1018  			accessToken := MakeTokenObject(jwt.MapClaims{
  1019  				"typ": nil,
  1020  				"exp": time.Now().Add(5 * time.Minute).Unix(),
  1021  			}).Raw
  1022  
  1023  			// Configure the server:
  1024  			server.AppendHandlers(
  1025  				CombineHandlers(
  1026  					VerifyClientCredentialsGrant("myclient", "mysecret"),
  1027  					RespondWithAccessToken(accessToken),
  1028  				),
  1029  			)
  1030  
  1031  			// Create the wrapper:
  1032  			wrapper, err := NewTransportWrapper().
  1033  				Logger(logger).
  1034  				TokenURL(server.URL()).
  1035  				TrustedCA(ca).
  1036  				Client("myclient", "mysecret").
  1037  				Build(ctx)
  1038  			Expect(err).ToNot(HaveOccurred())
  1039  			defer func() {
  1040  				err = wrapper.Close()
  1041  				Expect(err).ToNot(HaveOccurred())
  1042  			}()
  1043  
  1044  			// Get the tokens:
  1045  			returnedAccess, _, err := wrapper.Tokens(ctx)
  1046  			Expect(err).ToNot(HaveOccurred())
  1047  			Expect(returnedAccess).To(Equal(accessToken))
  1048  		})
  1049  
  1050  		It("Refreshes access token", func() {
  1051  			// Generate the tokens:
  1052  			expiredAccess := MakeTokenString("Bearer", -5*time.Second)
  1053  			validAccess := MakeTokenString("Bearer", 5*time.Minute)
  1054  
  1055  			// Configure the server:
  1056  			server.AppendHandlers(
  1057  				CombineHandlers(
  1058  					VerifyClientCredentialsGrant("myclient", "mysecret"),
  1059  					RespondWithAccessToken(validAccess),
  1060  				),
  1061  			)
  1062  
  1063  			// Create the wrapper:
  1064  			wrapper, err := NewTransportWrapper().
  1065  				Logger(logger).
  1066  				TokenURL(server.URL()).
  1067  				TrustedCA(ca).
  1068  				Client("myclient", "mysecret").
  1069  				Tokens(expiredAccess).
  1070  				Build(ctx)
  1071  			Expect(err).ToNot(HaveOccurred())
  1072  			defer func() {
  1073  				err = wrapper.Close()
  1074  				Expect(err).ToNot(HaveOccurred())
  1075  			}()
  1076  
  1077  			// Get the token:
  1078  			returnedAccess, _, err := wrapper.Tokens(ctx)
  1079  			Expect(err).ToNot(HaveOccurred())
  1080  			Expect(returnedAccess).To(Equal(validAccess))
  1081  		})
  1082  
  1083  		It("Fails with wrong client identifier", func() {
  1084  			// Configure the server:
  1085  			server.AppendHandlers(
  1086  				CombineHandlers(
  1087  					VerifyClientCredentialsGrant("badclient", "mysecret"),
  1088  					RespondWithTokenError("invalid_grant", "Bad client"),
  1089  				),
  1090  			)
  1091  
  1092  			// Create the wrapper:
  1093  			wrapper, err := NewTransportWrapper().
  1094  				Logger(logger).
  1095  				TokenURL(server.URL()).
  1096  				TrustedCA(ca).
  1097  				Client("badclient", "mysecret").
  1098  				Build(ctx)
  1099  			Expect(err).ToNot(HaveOccurred())
  1100  			defer func() {
  1101  				err = wrapper.Close()
  1102  				Expect(err).ToNot(HaveOccurred())
  1103  			}()
  1104  
  1105  			// Get the tokens:
  1106  			_, _, err = wrapper.Tokens(ctx)
  1107  			Expect(err).To(HaveOccurred())
  1108  		})
  1109  
  1110  		It("Fails with wrong client secret", func() {
  1111  			// Configure the server:
  1112  			server.AppendHandlers(
  1113  				CombineHandlers(
  1114  					VerifyClientCredentialsGrant("myclient", "badsecret"),
  1115  					RespondWithTokenError("invalid_grant", "Bad secret"),
  1116  				),
  1117  			)
  1118  
  1119  			// Create the wrapper:
  1120  			wrapper, err := NewTransportWrapper().
  1121  				Logger(logger).
  1122  				TokenURL(server.URL()).
  1123  				TrustedCA(ca).
  1124  				Client("myclient", "badsecret").
  1125  				Build(ctx)
  1126  			Expect(err).ToNot(HaveOccurred())
  1127  			defer func() {
  1128  				err = wrapper.Close()
  1129  				Expect(err).ToNot(HaveOccurred())
  1130  			}()
  1131  
  1132  			// Get the tokens:
  1133  			_, _, err = wrapper.Tokens(ctx)
  1134  			Expect(err).To(HaveOccurred())
  1135  		})
  1136  
  1137  		It("Honours cookies", func() {
  1138  			// Generate the tokens:
  1139  			expiredAccess := MakeTokenString("Bearer", -5*time.Minute)
  1140  			validAccess := MakeTokenString("Bearer", 5*time.Minute)
  1141  			refreshToken := MakeTokenString("Refresh", 10*time.Hour)
  1142  
  1143  			// Configure the server:
  1144  			server.AppendHandlers(
  1145  				CombineHandlers(
  1146  					RespondWithCookie("mycookie", "myvalue"),
  1147  					RespondWithAccessAndRefreshTokens(expiredAccess, refreshToken),
  1148  				),
  1149  				CombineHandlers(
  1150  					VerifyCookie("mycookie", "myvalue"),
  1151  					RespondWithAccessAndRefreshTokens(validAccess, refreshToken),
  1152  				),
  1153  			)
  1154  
  1155  			// Create the wrapper:
  1156  			wrapper, err := NewTransportWrapper().
  1157  				Logger(logger).
  1158  				TokenURL(server.URL()).
  1159  				TrustedCA(ca).
  1160  				Client("myclient", "mysecret").
  1161  				Build(ctx)
  1162  			Expect(err).ToNot(HaveOccurred())
  1163  			defer func() {
  1164  				err = wrapper.Close()
  1165  				Expect(err).ToNot(HaveOccurred())
  1166  			}()
  1167  
  1168  			// Request the tokens the first time. This will return an expired access
  1169  			// token and a valid refresh token.
  1170  			_, _, err = wrapper.Tokens(ctx)
  1171  			Expect(err).ToNot(HaveOccurred())
  1172  
  1173  			// Request the tokens a second time, therefore forcing a refresh grant which
  1174  			// should honor the cookies returned in the first attempt:
  1175  			_, _, err = wrapper.Tokens(ctx)
  1176  			Expect(err).ToNot(HaveOccurred())
  1177  		})
  1178  
  1179  		It("Doesn't fail if the server returns a refresh token", func() {
  1180  			// Generate the tokens:
  1181  			accessToken := MakeTokenString("Bearer", 5*time.Minute)
  1182  			refreshToken := MakeTokenString("Refresh", 10*time.Hour)
  1183  
  1184  			// Configure the server:
  1185  			server.AppendHandlers(
  1186  				CombineHandlers(
  1187  					VerifyClientCredentialsGrant("myclient", "mysecret"),
  1188  					RespondWithAccessAndRefreshTokens(accessToken, refreshToken),
  1189  				),
  1190  			)
  1191  
  1192  			// Create the wrapper:
  1193  			wrapper, err := NewTransportWrapper().
  1194  				Logger(logger).
  1195  				TokenURL(server.URL()).
  1196  				TrustedCA(ca).
  1197  				Client("myclient", "mysecret").
  1198  				Build(ctx)
  1199  			Expect(err).ToNot(HaveOccurred())
  1200  			defer func() {
  1201  				err = wrapper.Close()
  1202  				Expect(err).ToNot(HaveOccurred())
  1203  			}()
  1204  
  1205  			// Get the token:
  1206  			returnedAccess, returnedRefresh, err := wrapper.Tokens(ctx)
  1207  			Expect(err).ToNot(HaveOccurred())
  1208  			Expect(returnedAccess).To(Equal(accessToken))
  1209  			Expect(returnedRefresh).To(Equal(refreshToken))
  1210  		})
  1211  
  1212  		It("Uses client credentials grant even if it has refresh token", func() {
  1213  			// Generate the tokens:
  1214  			expiredAccess := MakeTokenString("Bearer", -5*time.Minute)
  1215  			validAccess := MakeTokenString("Bearer", 5*time.Minute)
  1216  			validRefresh := MakeTokenString("Refresh", 10*time.Hour)
  1217  
  1218  			// Configure the server so that it returns a expired access token and a
  1219  			// valid refresh token for the first request, and then a valid access token
  1220  			// for the second request. In both cases the client should be using the
  1221  			// client credentials grant.
  1222  			server.AppendHandlers(
  1223  				CombineHandlers(
  1224  					VerifyClientCredentialsGrant("myclient", "mysecret"),
  1225  					RespondWithAccessAndRefreshTokens(expiredAccess, validRefresh),
  1226  				),
  1227  				CombineHandlers(
  1228  					VerifyClientCredentialsGrant("myclient", "mysecret"),
  1229  					RespondWithAccessAndRefreshTokens(validAccess, validRefresh),
  1230  				),
  1231  			)
  1232  
  1233  			// Create the wrapper:
  1234  			wrapper, err := NewTransportWrapper().
  1235  				Logger(logger).
  1236  				TokenURL(server.URL()).
  1237  				TrustedCA(ca).
  1238  				Client("myclient", "mysecret").
  1239  				Tokens(expiredAccess, validRefresh).
  1240  				Build(ctx)
  1241  			Expect(err).ToNot(HaveOccurred())
  1242  			defer func() {
  1243  				err = wrapper.Close()
  1244  				Expect(err).ToNot(HaveOccurred())
  1245  			}()
  1246  
  1247  			// Force the initial token request. This will return an expired access token
  1248  			// and a valid refresh token, that way when we get the tokens again the
  1249  			// wrapper should send another request, but using the client credentials
  1250  			// grant and ignoring the refresh token.
  1251  			returnedAccess, returnedRefresh, err := wrapper.Tokens(ctx)
  1252  			Expect(err).ToNot(HaveOccurred())
  1253  			Expect(returnedAccess).To(Equal(expiredAccess))
  1254  			Expect(returnedRefresh).To(Equal(validRefresh))
  1255  
  1256  			// Force another request:
  1257  			returnedAccess, returnedRefresh, err = wrapper.Tokens(ctx)
  1258  			Expect(err).ToNot(HaveOccurred())
  1259  			Expect(returnedAccess).To(Equal(validAccess))
  1260  			Expect(returnedRefresh).To(Equal(validRefresh))
  1261  		})
  1262  
  1263  		It("Uses client credentials grant with basic authentication and opaque refresh token", func() {
  1264  			// Generate the tokens:
  1265  			expiredAccess := MakeTokenString("Bearer", -5*time.Minute)
  1266  			validAccess := MakeTokenString("Bearer", 5*time.Minute)
  1267  			opaqueRefresh := "a.bb.ccc.dddd.eeeee"
  1268  
  1269  			// Configure the server so that it returns a expired access token for the
  1270  			// first request, and then a valid access token for the second request. In
  1271  			// both cases the client should be using the client credentials grant with
  1272  			// basic authentication.
  1273  			server.AppendHandlers(
  1274  				CombineHandlers(
  1275  					VerifyClientCredentialsGrant("myclient", "mysecret"),
  1276  					RespondWithAccessToken(expiredAccess),
  1277  				),
  1278  				CombineHandlers(
  1279  					VerifyClientCredentialsGrant("myclient", "mysecret"),
  1280  					RespondWithAccessToken(validAccess),
  1281  				),
  1282  			)
  1283  
  1284  			// Create the wrapper:
  1285  			wrapper, err := NewTransportWrapper().
  1286  				Logger(logger).
  1287  				TokenURL(server.URL()).
  1288  				TrustedCA(ca).
  1289  				Client("myclient", "mysecret").
  1290  				Tokens(expiredAccess, opaqueRefresh).
  1291  				Build(ctx)
  1292  			Expect(err).ToNot(HaveOccurred())
  1293  			defer func() {
  1294  				err = wrapper.Close()
  1295  				Expect(err).ToNot(HaveOccurred())
  1296  			}()
  1297  
  1298  			// Force the initial token request. This will return an expired access token
  1299  			// and a valid refresh token, that way when we get the tokens again the
  1300  			// wrapper should send another request, but using the client credentials
  1301  			// grant and ignoring the refresh token.
  1302  			returnedAccess, returnedRefresh, err := wrapper.Tokens(ctx)
  1303  			Expect(err).ToNot(HaveOccurred())
  1304  			Expect(returnedAccess).To(Equal(expiredAccess))
  1305  			Expect(returnedRefresh).To(Equal(opaqueRefresh))
  1306  
  1307  			// Force another request:
  1308  			returnedAccess, returnedRefresh, err = wrapper.Tokens(ctx)
  1309  			Expect(err).ToNot(HaveOccurred())
  1310  			Expect(returnedAccess).To(Equal(validAccess))
  1311  			Expect(returnedRefresh).To(Equal(opaqueRefresh))
  1312  		})
  1313  	})
  1314  
  1315  	Describe("Retry for getting access token", func() {
  1316  		It("Return access token after a few retries", func() {
  1317  			// Generate tokens:
  1318  			refreshToken := MakeTokenString("Refresh", 10*time.Hour)
  1319  			accessToken := MakeTokenString("Bearer", 5*time.Minute)
  1320  
  1321  			server.AppendHandlers(
  1322  				RespondWithContent(
  1323  					http.StatusInternalServerError,
  1324  					"text/plain",
  1325  					"Internal Server Error",
  1326  				),
  1327  				RespondWithContent(
  1328  					http.StatusBadGateway,
  1329  					"text/plain",
  1330  					"Bad Gateway",
  1331  				),
  1332  				CombineHandlers(
  1333  					VerifyRefreshGrant(refreshToken),
  1334  					RespondWithAccessAndRefreshTokens(accessToken, refreshToken),
  1335  				),
  1336  			)
  1337  
  1338  			// Create the wrapper:
  1339  			wrapper, err := NewTransportWrapper().
  1340  				Logger(logger).
  1341  				TokenURL(server.URL()).
  1342  				TrustedCA(ca).
  1343  				Tokens(refreshToken).
  1344  				Build(ctx)
  1345  			Expect(err).ToNot(HaveOccurred())
  1346  			defer func() {
  1347  				err = wrapper.Close()
  1348  				Expect(err).ToNot(HaveOccurred())
  1349  			}()
  1350  
  1351  			// Get the tokens:
  1352  			returnedAccess, returnedRefresh, err := wrapper.Tokens(ctx)
  1353  			Expect(err).ToNot(HaveOccurred())
  1354  			Expect(returnedAccess).ToNot(BeEmpty())
  1355  			Expect(returnedRefresh).ToNot(BeEmpty())
  1356  		})
  1357  
  1358  		It("Test no retry when status is not http 5xx", func() {
  1359  			// Generate tokens:
  1360  			refreshToken := MakeTokenString("Refresh", 10*time.Hour)
  1361  			accessToken := MakeTokenString("Bearer", 5*time.Minute)
  1362  
  1363  			server.AppendHandlers(
  1364  				RespondWithContent(
  1365  					http.StatusInternalServerError,
  1366  					"text/plain",
  1367  					"Internal Server Error",
  1368  				),
  1369  				RespondWithJSON(
  1370  					http.StatusForbidden,
  1371  					"{}",
  1372  				),
  1373  				CombineHandlers(
  1374  					VerifyRefreshGrant(refreshToken),
  1375  					RespondWithAccessAndRefreshTokens(accessToken, refreshToken),
  1376  				),
  1377  			)
  1378  
  1379  			// Create the wrapper:
  1380  			wrapper, err := NewTransportWrapper().
  1381  				Logger(logger).
  1382  				TokenURL(server.URL()).
  1383  				TrustedCA(ca).
  1384  				Tokens(refreshToken).
  1385  				Build(ctx)
  1386  			Expect(err).ToNot(HaveOccurred())
  1387  			defer func() {
  1388  				err = wrapper.Close()
  1389  				Expect(err).ToNot(HaveOccurred())
  1390  			}()
  1391  
  1392  			// Get the tokens:
  1393  			_, _, err = wrapper.Tokens(ctx)
  1394  			Expect(err).To(HaveOccurred())
  1395  		})
  1396  
  1397  		It("Honours context timeout", func() {
  1398  			// Generate tokens:
  1399  			refreshToken := MakeTokenString("Refresh", 10*time.Hour)
  1400  			accessToken := MakeTokenString("Bearer", 5*time.Minute)
  1401  
  1402  			// Configure the server with a handler that introduces an
  1403  			// artificial delay:
  1404  			server.AppendHandlers(
  1405  				CombineHandlers(
  1406  					http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
  1407  						time.Sleep(10 * time.Millisecond)
  1408  					}),
  1409  					VerifyRefreshGrant(refreshToken),
  1410  					RespondWithAccessAndRefreshTokens(accessToken, refreshToken),
  1411  				),
  1412  			)
  1413  
  1414  			// Create the wrapper:
  1415  			wrapper, err := NewTransportWrapper().
  1416  				Logger(logger).
  1417  				TokenURL(server.URL()).
  1418  				TrustedCA(ca).
  1419  				Tokens(refreshToken).
  1420  				Build(ctx)
  1421  			Expect(err).ToNot(HaveOccurred())
  1422  			defer func() {
  1423  				err = wrapper.Close()
  1424  				Expect(err).ToNot(HaveOccurred())
  1425  			}()
  1426  
  1427  			// Request the token with a timeout smaller than the artificial
  1428  			// delay introduced by the server:
  1429  			ctx, cancel := context.WithTimeout(ctx, 5*time.Millisecond)
  1430  			defer cancel()
  1431  			_, _, err = wrapper.Tokens(ctx)
  1432  
  1433  			// The request should fail with a context deadline exceeded error:
  1434  			Expect(err).To(HaveOccurred())
  1435  			Expect(errors.Is(err, context.DeadlineExceeded)).To(BeTrue())
  1436  		})
  1437  	})
  1438  
  1439  	When("Parsing a pull-secret access token", func() {
  1440  		It("Will not consider a JWT as a pull-secret access token", func() {
  1441  			refreshToken := MakeTokenString("Refresh", 10*time.Hour)
  1442  			accessToken := MakeTokenString("Bearer", 5*time.Minute)
  1443  			err := parsePullSecretAccessToken(refreshToken)
  1444  			Expect(err).To(HaveOccurred())
  1445  			err = parsePullSecretAccessToken(accessToken)
  1446  			Expect(err).To(HaveOccurred())
  1447  		})
  1448  		It("Will validate a correctly-formatted token", func() {
  1449  			pullSecretAccessToken := makeTestPullSecretToken()
  1450  			err := parsePullSecretAccessToken(pullSecretAccessToken)
  1451  			Expect(err).NotTo(HaveOccurred())
  1452  		})
  1453  	})
  1454  
  1455  	It("Will prefer the pull-secret access token if one is supplied", func() {
  1456  		// Generate the tokens:
  1457  		refreshToken := MakeTokenString("Refresh", 10*time.Hour)
  1458  		accessToken := MakeTokenString("Bearer", 5*time.Minute)
  1459  		pullSecretAccessToken := makeTestPullSecretToken()
  1460  
  1461  		// Create the wrapper:
  1462  		wrapper, err := NewTransportWrapper().
  1463  			Logger(logger).
  1464  			TokenURL(server.URL()).
  1465  			TrustedCA(ca).
  1466  			Tokens(accessToken, refreshToken, pullSecretAccessToken).
  1467  			Build(ctx)
  1468  		Expect(err).ToNot(HaveOccurred())
  1469  		defer func() {
  1470  			err = wrapper.Close()
  1471  			Expect(err).ToNot(HaveOccurred())
  1472  		}()
  1473  
  1474  		// Get the tokens:
  1475  		returnedAccess, _, err := wrapper.Tokens(ctx)
  1476  		Expect(err).ToNot(HaveOccurred())
  1477  		Expect(returnedAccess).To(Equal(pullSecretAccessToken), "Pull Secret Access Token not returned")
  1478  	})
  1479  
  1480  })
  1481  
  1482  func makeTestPullSecretToken() string {
  1483  	id := uuid.New()
  1484  	dummyTokenText := base64.StdEncoding.EncodeToString([]byte("abcdefghijklmnopqrstuvwxyz"))
  1485  	return fmt.Sprintf("%s:%s", id, dummyTokenText)
  1486  }
  1487  
  1488  func VerifyPasswordGrant(user, password string) http.HandlerFunc {
  1489  	return CombineHandlers(
  1490  		VerifyRequest(http.MethodPost, "/"),
  1491  		VerifyContentType("application/x-www-form-urlencoded"),
  1492  		VerifyFormKV("grant_type", "password"),
  1493  		VerifyFormKV("username", user),
  1494  		VerifyFormKV("password", password),
  1495  	)
  1496  }
  1497  
  1498  func VerifyClientCredentialsGrant(id, secret string) http.HandlerFunc {
  1499  	return CombineHandlers(
  1500  		VerifyRequest(http.MethodPost, "/"),
  1501  		VerifyContentType("application/x-www-form-urlencoded"),
  1502  		VerifyBasicAuth(id, secret),
  1503  		VerifyFormKV("grant_type", "client_credentials"),
  1504  		VerifyFormKV("client_id", id),
  1505  	)
  1506  }
  1507  
  1508  func VerifyRefreshGrant(refreshToken string) http.HandlerFunc {
  1509  	return CombineHandlers(
  1510  		VerifyRequest(http.MethodPost, "/"),
  1511  		VerifyContentType("application/x-www-form-urlencoded"),
  1512  		VerifyFormKV("grant_type", "refresh_token"),
  1513  		VerifyFormKV("refresh_token", refreshToken),
  1514  	)
  1515  }