github.com/franc20/ayesa_sap@v7.0.0-beta.28.0.20200124003224-302d4d52fa6c+incompatible/api/cloudcontroller/wrapper/uaa_authentication.go (about) 1 package wrapper 2 3 import ( 4 "strings" 5 "time" 6 7 "github.com/SermoDigital/jose/jws" 8 9 "code.cloudfoundry.org/cli/api/cloudcontroller" 10 "code.cloudfoundry.org/cli/api/uaa" 11 ) 12 13 //go:generate counterfeiter . UAAClient 14 15 const accessTokenExpirationMargin = time.Minute 16 17 // UAAClient is the interface for getting a valid access token 18 type UAAClient interface { 19 RefreshAccessToken(refreshToken string) (uaa.RefreshedTokens, error) 20 } 21 22 //go:generate counterfeiter . TokenCache 23 24 // TokenCache is where the UAA token information is stored. 25 type TokenCache interface { 26 AccessToken() string 27 RefreshToken() string 28 SetAccessToken(token string) 29 SetRefreshToken(token string) 30 } 31 32 // UAAAuthentication wraps connections and adds authentication headers to all 33 // requests 34 type UAAAuthentication struct { 35 connection cloudcontroller.Connection 36 client UAAClient 37 cache TokenCache 38 } 39 40 // NewUAAAuthentication returns a pointer to a UAAAuthentication wrapper with 41 // the client and a token cache. 42 func NewUAAAuthentication(client UAAClient, cache TokenCache) *UAAAuthentication { 43 return &UAAAuthentication{ 44 client: client, 45 cache: cache, 46 } 47 } 48 49 // Make adds authentication headers to the passed in request and then calls the 50 // wrapped connection's Make. If the client is not set on the wrapper, it will 51 // not add any header or handle any authentication errors. 52 func (t *UAAAuthentication) Make(request *cloudcontroller.Request, passedResponse *cloudcontroller.Response) error { 53 if t.client == nil { 54 return t.connection.Make(request, passedResponse) 55 } 56 57 if t.cache.AccessToken() != "" || t.cache.RefreshToken() != "" { 58 // assert a valid access token for authenticated requests 59 err := t.refreshToken() 60 if nil != err { 61 return err 62 } 63 request.Header.Set("Authorization", t.cache.AccessToken()) 64 } 65 66 err := t.connection.Make(request, passedResponse) 67 return err 68 } 69 70 // SetClient sets the UAA client that the wrapper will use. 71 func (t *UAAAuthentication) SetClient(client UAAClient) { 72 t.client = client 73 } 74 75 // Wrap sets the connection on the UAAAuthentication and returns itself 76 func (t *UAAAuthentication) Wrap(innerconnection cloudcontroller.Connection) cloudcontroller.Connection { 77 t.connection = innerconnection 78 return t 79 } 80 81 // refreshToken refreshes the JWT access token if it is expired or about to expire. 82 // If the access token is not yet expired, no action is performed. 83 func (t *UAAAuthentication) refreshToken() error { 84 var expiresIn time.Duration 85 86 tokenStr := strings.TrimPrefix(t.cache.AccessToken(), "bearer ") 87 token, err := jws.ParseJWT([]byte(tokenStr)) 88 89 if err == nil { 90 expiration, ok := token.Claims().Expiration() 91 if ok { 92 expiresIn = time.Until(expiration) 93 } 94 } 95 96 if err != nil || expiresIn < accessTokenExpirationMargin { 97 tokens, err := t.client.RefreshAccessToken(t.cache.RefreshToken()) 98 if err != nil { 99 return err 100 } 101 t.cache.SetAccessToken(tokens.AuthorizationToken()) 102 t.cache.SetRefreshToken(tokens.RefreshToken) 103 } 104 return nil 105 }