Skip to content

Commit

Permalink
design proposal for refresh token authenticator
Browse files Browse the repository at this point in the history
  • Loading branch information
dikhan committed Oct 3, 2019
1 parent 6bad267 commit f91faa8
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 4 deletions.
1 change: 1 addition & 0 deletions examples/swaggercodegen/api/resources/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ securityDefinitions:
apikey_auth: # basic apikey header auth, the header name will be the value used in the name property, in this case "Authorization", when calling the applicable resource API
type: "apiKey"
name: "Authorization"
x-terraform-refresh-token-url: "https://api.iam.com/v1/auth/token"
in: "header"
# apikey_header_auth_bearer: // example of header auth using the bearer schema as per the specification
# type: "apiKey"
Expand Down
3 changes: 3 additions & 0 deletions openapi/openapi_spec_authenticator_apikey.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ type specAPIKeyAuthenticator interface {
func createAPIKeyAuthenticator(secDef SpecSecurityDefinition, value string) specAPIKeyAuthenticator {
switch secDef.getAPIKey().In {
case inHeader:
if secDef.getType() == securityDefinitionAPIKeyRefreshToken {
return newAPIRefreshTokenAuthenticator(secDef.getAPIKey().Name, secDef.buildValue(value), secDef.getAPIKey().Metadata[refreshTokenURLKey].(string))
}
return newAPIKeyHeaderAuthenticator(secDef.getAPIKey().Name, secDef.buildValue(value))
case inQuery:
return newAPIKeyQueryAuthenticator(secDef.getAPIKey().Name, secDef.buildValue(value))
Expand Down
44 changes: 44 additions & 0 deletions openapi/openapi_spec_authenticator_refresh_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package openapi

import (
"fmt"
)

// Api Key Header Auth
type apiRefreshTokenAuthenticator struct {
apiKey
refreshTokenURL string
}

func newAPIRefreshTokenAuthenticator(name, refreshToken, refreshTokenURL string) apiRefreshTokenAuthenticator {
return apiRefreshTokenAuthenticator{
apiKey: apiKey{
name: name,
value: refreshToken,
},
refreshTokenURL: refreshTokenURL,
}
}

func (a apiRefreshTokenAuthenticator) getContext() interface{} {
return a.apiKey
}

func (a apiRefreshTokenAuthenticator) getType() authType {
return authTypeAPIKeyHeader
}

// prepareAPIKeyAuthentication adds to the map the auth header required for apikey header authentication. The url
// remains the same
func (a apiRefreshTokenAuthenticator) prepareAuth(authContext *authContext) error {
apiKey := a.getContext().(apiKey)

authorizationHeaderValue := apiKey.value
fmt.Println(authorizationHeaderValue)
// TODO: call refresh token API (POST) a.refreshTokenURL including the refresh token passed in as Authorization header with value apiKey.value
// Get the access token from the response header Authorization
// Strip out the bearer scheme from the header value and return the string

authContext.headers[apiKey.name] = "access token"
return nil
}
54 changes: 54 additions & 0 deletions openapi/openapi_spec_security_definition_apikey_refresh_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package openapi

import (
"fmt"
"github.com/dikhan/terraform-provider-openapi/openapi/terraformutils"
"github.com/go-openapi/spec"
"strings"
)

type specAPIKeyHeaderCustomAuthSecurityDefinition struct {
name string
secDef *spec.SecurityScheme
refreshTokenURL string
}

// newAPIKeyHeaderBearerSecurityDefinition constructs a SpecSecurityDefinition of Header type using the Bearer authentication
// scheme. The secDefName value is the identifier of the security definition, and the apiKeyName is the actual value of the header/query that will be user in the HTTP request.
func newAPIKeyHeaderRefreshTokenSecurityDefinition(secDefName string, secDef *spec.SecurityScheme, refreshTokenURL string) specAPIKeyHeaderCustomAuthSecurityDefinition {
return specAPIKeyHeaderCustomAuthSecurityDefinition{secDefName, secDef, refreshTokenURL}
}

func (s specAPIKeyHeaderCustomAuthSecurityDefinition) getName() string {
return s.name
}

func (s specAPIKeyHeaderCustomAuthSecurityDefinition) getType() securityDefinitionType {
return securityDefinitionAPIKeyRefreshToken
}

func (s specAPIKeyHeaderCustomAuthSecurityDefinition) getTerraformConfigurationName() string {
return terraformutils.ConvertToTerraformCompliantName(s.name)
}

func (s specAPIKeyHeaderCustomAuthSecurityDefinition) getAPIKey() specAPIKey {
apiKey := newAPIKeyHeader(authorization)
apiKey.Metadata = map[apiKeyMetadataKey]interface{}{
refreshTokenURLKey: s.refreshTokenURL,
}
return apiKey
}

func (s specAPIKeyHeaderCustomAuthSecurityDefinition) buildValue(refreshToken string) string {
if !strings.Contains(refreshToken, bearerScheme) {
refreshToken = fmt.Sprintf("Bearer %s", refreshToken)
}
return refreshToken
}

func (s specAPIKeyHeaderCustomAuthSecurityDefinition) validate() error {
if s.name == "" {
return fmt.Errorf("specAPIKeyHeaderBearerSecurityDefinition missing mandatory security definition name")
}
return nil
}
11 changes: 9 additions & 2 deletions openapi/openapi_spec_security_definition_spec_apikey.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,16 @@ const (
inQuery apiKeyIn = "query"
)

type apiKeyMetadataKey string

const (
refreshTokenURLKey apiKeyMetadataKey = "refreshTokenURL"
)

type specAPIKey struct {
In apiKeyIn
Name string
In apiKeyIn
Name string
Metadata map[apiKeyMetadataKey]interface{}
}

func newAPIKeyHeader(name string) specAPIKey {
Expand Down
3 changes: 2 additions & 1 deletion openapi/openapi_spec_security_definitions.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ func (s SpecSecurityDefinitions) findSecurityDefinitionFor(securitySchemeName st
type securityDefinitionType string

const (
securityDefinitionAPIKey securityDefinitionType = "apiKey"
securityDefinitionAPIKey securityDefinitionType = "apiKey"
securityDefinitionAPIKeyRefreshToken securityDefinitionType = "apiKeyRefreshToken"
)

// SpecSecurityDefinition defines the behaviour expected for security definition implementations. This interface creates
Expand Down
13 changes: 12 additions & 1 deletion openapi/openapi_v2_security.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
)

const extTfAuthenticationSchemeBearer = "x-terraform-authentication-scheme-bearer"
const extTfAuthenticationRefreshToken = "x-terraform-refresh-token-url"

type specV2Security struct {
SecurityDefinitions spec.SecurityDefinitions
Expand All @@ -21,7 +22,9 @@ func (s *specV2Security) GetAPIKeySecurityDefinitions() (*SpecSecurityDefinition
var securityDefinition SpecSecurityDefinition
switch secDef.In {
case "header":
if s.isBearerScheme(secDef) {
if refreshTokenURL := s.isRefreshTokenAuth(secDef); refreshTokenURL != "" {
securityDefinition = newAPIKeyHeaderRefreshTokenSecurityDefinition(secDefName, secDef, refreshTokenURL)
} else if s.isBearerScheme(secDef) {
securityDefinition = newAPIKeyHeaderBearerSecurityDefinition(secDefName)
} else {
securityDefinition = newAPIKeyHeaderSecurityDefinition(secDefName, secDef.Name)
Expand Down Expand Up @@ -52,6 +55,14 @@ func (s *specV2Security) isBearerScheme(secDef *spec.SecurityScheme) bool {
return false
}

func (s *specV2Security) isRefreshTokenAuth(secDef *spec.SecurityScheme) string {
refreshTokenURL, isRefreshTokenAuth := secDef.Extensions.GetString(extTfAuthenticationRefreshToken)
if isRefreshTokenAuth {
return refreshTokenURL
}
return ""
}

// GetGlobalSecuritySchemes returns a list of SpecSecuritySchemes that have their corresponding SpecSecurityDefinition
func (s *specV2Security) GetGlobalSecuritySchemes() (SpecSecuritySchemes, error) {
securitySchemes := createSecuritySchemes(s.GlobalSecurity)
Expand Down

0 comments on commit f91faa8

Please sign in to comment.