Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow customizing httpBearerAuth scheme #872

Closed
glb opened this issue Jul 28, 2021 · 11 comments
Closed

Allow customizing httpBearerAuth scheme #872

glb opened this issue Jul 28, 2021 · 11 comments
Assignees

Comments

@glb
Copy link

glb commented Jul 28, 2021

I'd like to model a service that uses what is effectively the HTTP Bearer authentication mechanism but with a different scheme. Would it be possible to extend the httpBearerAuth trait to support this?

I'm thinking something like this:

@httpBearerAuth(scheme="ApiKey")

which would result in HTTP requests with an Authorization: ApiKey {key} header. Existing services or services that use the Bearer scheme would continue to use @httpBearerAuth without the parameter -- I'm hoping that's possible but am still new to the Smithy syntax.

Is this possible? Is it a bad idea? Is there a better way to accomplish this? I think the alternative is to create a new @authDefinition that does the right thing, but I don't particularly want to reinvent all of the surrounding codegen ecosystem just to change from Bearer to ApiKey. On a related note, I recognize that existing codegen would need to be updated to support this, where would I go to learn more?

@kstich
Copy link
Contributor

kstich commented Jul 29, 2021

Can you share a bit more information on where the requirement to have ApiKey as the scheme comes from?

The @httpBearerAuth trait indicates RFC6750 compliant auth, which has a pretty explicit requirement that this is Bearer.

@glb
Copy link
Author

glb commented Jul 29, 2021

@kstich sure ... there's an existing service I'd like to model using smithy that requires the Authorization header to be ApiKey {key} (no {} of course). I'm super-new to smithy so I'm not sure what the best path to end-to-end support for this would be, but it seems like the closest approximation starts here. As I mentioned in the original description, I think the next-best alternative is to create a new @authDefinition trait and annotate the service with that trait instead of @httpBearerAuth, which I think would require implementing something to translate that trait into the appropriate behaviour in the client code.

I'm also curious to understand how a generated client for a service with the @httpBearerAuth trait would be given the bearer token. I'm sure it's different for each language, but I'd love to learn more about how to generate and customize client code from a smithy model -- would https://github.com/awslabs/smithy-typescript be a good place to start getting a rough idea?

@mtdowling
Copy link
Member

I think another good option is to add scheme to httpApiKeyAuth:

@httpApiKeyAuth(scheme: "ApiKey")

When used, it would just put "ApiKey" + " " in front of the provided key value for the auth scheme (i.e., x-whatever: ApiKey the-value).

would https://github.com/awslabs/smithy-typescript be a good place to start getting a rough idea?

It would! But I think the change I'm suggesting would mean you wouldn't need to do any custom codegen work.

@glb
Copy link
Author

glb commented Aug 9, 2021

@mtdowling that sounds fantastic, love it! How can I help?

@DavidOgunsAWS
Copy link
Contributor

@glb Have a look at that pull request. I think it enables your use-case

@glb
Copy link
Author

glb commented Aug 18, 2021

Awesome, thank you!

@glb
Copy link
Author

glb commented Aug 19, 2021

@DavidOgunsAWS @mtdowling a follow-up question about this. I updated my model to add httpApiKeyAuth and httpBearerAuth:

@httpApiKeyAuth(scheme: "ApiKey", name: "Authorization", in: "header")
@httpBearerAuth
@auth([httpApiKeyAuth, httpBearerAuth])
service Example {
    version: "2021-08-19",
    resources: [...]
}

and ran a build to generate an OpenAPI description of the API. I was a little surprised that it didn't fail because of course the changes aren't there yet to support scheme in @httpApiKeyAuth, but I got this in the securitySchemes section:

securitySchemes from generated OpenAPI description
        "securitySchemes": {
            "smithy.api.httpApiKeyAuth": {
                "type": "apiKey",
                "description": "X-Api-Key authentication",
                "name": "Authorization",
                "in": "header"
            },
            "smithy.api.httpBearerAuth": {
                "type": "http",
                "description": "HTTP Bearer authentication",
                "scheme": "Bearer"
            }
        }

It seems like a more accurate translation in this case would be:

securitySchemes from generated OpenAPI description
        "securitySchemes": {
            "smithy.api.httpApiKeyAuth": {
                "type": "http",
                "description": "ApiKey authentication",
                "scheme": "ApiKey"
            },
            "smithy.api.httpBearerAuth": {
                "type": "http",
                "description": "HTTP Bearer authentication",
                "scheme": "Bearer"
            }
        }

i.e. using type: http and scheme: {scheme} when header is Authorization and scheme is set, and maybe setting the description a bit differently.

This conclusion seems to be supported by the OpenAPI specification of securitySchemes, which says (excerpted):

Field Name Type Applies To Description
type string Any REQUIRED. The type of the security scheme. Valid values are "apiKey", "http", "mutualTLS", "oauth2", "openIdConnect".
scheme string http REQUIRED. The name of the HTTP Authorization scheme to be used in the Authorization header as defined in RFC7235. The values used SHOULD be registered in the IANA Authentication Scheme registry.

Thoughts (other than the obvious "ApiKey isn't registered in the IANA Authentication Scheme registry")?

If you agree that the OpenAPI generation should change, where should I follow up?

@glb
Copy link
Author

glb commented Sep 3, 2021

Thanks @DavidOgunsAWS ! Now that the PR is merged, does there need to be a release so that the new feature is available, or is there a way for me to pick up a snapshot build? I'm sorry, I'm not very familiar with Gradle so I've just been following the instructions to get things working. I'm assuming I'll need to make changes to my build.gradle.kts which currently looks like this:

plugins {
    java
    id("software.amazon.smithy").version("0.5.3")
}

repositories {
    mavenLocal()
    mavenCentral()
}

buildscript {
    dependencies {
        classpath("software.amazon.smithy:smithy-openapi:1.10.0")
        // The openapi plugin configured in the smithy-build.json example below
        // uses the restJson1 protocol defined in the aws-traits package. This
        // additional dependency must added to use that protocol.
        classpath("software.amazon.smithy:smithy-aws-traits:1.10.0")
    }
}

dependencies {
    // The dependency for restJson1 is required here too.
    implementation("software.amazon.smithy:smithy-aws-traits:1.10.0")
}

I've managed to piece this together to get an OpenAPI artifact; would love to understand what the steps are to get a proper client, maybe using awslabs/smithy-typescript, but I'm sure that's out of scope for this repository.

@mtdowling
Copy link
Member

mtdowling commented Oct 11, 2021

Hey @glb, sorry for the delay in responding. This was released in 1.12.0 on 2021-10-05, so you don't need to do anything fancy in Gradle to use these updates.

Regarding the generated security scheme:

  • We should update the generated description to not claim "X-Api-Key authentication"
  • type, name, and in seem fine to me.
  • (edit) We do include the scheme in the generated OpenAPI if one is provided on the trait.

Thoughts (other than the obvious "ApiKey isn't registered in the IANA Authentication Scheme registry")?

Ah, yeah, we're missing scheme, and we could theoretically create one that isn't registered with the IANA. Based on examples in the spec and supplemental documentation, it doesn't appear that scheme is actually required.

I think the documentation given for scheme is misleading. They mark lots of stuff as required, but they're only conditionally required.

REQUIRED. The name of the HTTP Authorization scheme to be used in the Authorization header as defined in RFC7235. The values used SHOULD be registered in the IANA Authentication Scheme registry.

would love to understand what the steps are to get a proper client, maybe using awslabs/smithy-typescript, but I'm sure that's out of scope for this repository.

I think this would be a great feature request on the smithy-typescript repo: https://github.com/awslabs/smithy-typescript. If you're interested in contributing this, then opening an issue could also be an opportunity to get pointers for how to implement it.

@mtdowling
Copy link
Member

With the changes made in #934 to improve the description, I think this issue is resolved now, and followup work to add support to the TypeScript codegen can be tracked at https://github.com/awslabs/smithy-typescript

@glb
Copy link
Author

glb commented Oct 12, 2021

Thanks @mtdowling !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants