golang / oauth2

Go OAuth2
https://golang.org/x/oauth2
BSD 3-Clause "New" or "Revised" License
5.36k stars 988 forks source link

Implementing "client_assertion" #744

Open naizerjohn-ms opened 1 week ago

naizerjohn-ms commented 1 week ago

Hello!

Currently this package does not support client_assertion/client_assertion_type OAuth2.0 client authentication outlined here in the OpenID Connect documentation (not up to standard). Here is an example outlined in this documentation, for a visual on what the request would look like:

  Host: server.example.com
  Content-Type: application/x-www-form-urlencoded

  grant_type=authorization_code&
    code=i1WsRn1uB1&
    client_id=s6BhdRkqt3&
    client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&
    client_assertion=PHNhbWxwOl ... ZT

as opposed to what this package only currently supports (client_secret):

  Host: server.example.com
  Content-Type: application/x-www-form-urlencoded

  grant_type=authorization_code&
    code=i1WsRn1uB1&
    client_id=s6BhdRkqt3&
    client_secret=PHNhbWxwOl ... ZT

I am willing to work towards this implementation and am asking for any support/guidance for achieving this solution. Many tech companies (including ours) are migrating away from using secrets and towards more secure authentication systems. Please see this article which provides a deeper description on what client_assertions are as well.

zetaab commented 1 week ago

we are interested of this feature as well.

naizerjohn-ms commented 1 week ago

Hi @zetaab! I see that you have made some contributions to client_assertion is the client credentials flow! However, this feature also needs to be added into the 'oauth2/internal' directory for the auth code grant flow. The auth code grant flow retrieves both an access token and an id token, the ideal grant flow for SSO (user + app). See this link for more info on this grant flow.

zetaab commented 1 week ago

@naizerjohn-ms that can be done already?

Example (disclaimer: I did not test this but afaik when reading code it should be possible)

    conf := &oauth2.Config{
        ClientID:     authConf.ClientID,
        Endpoint:     provider.Endpoint(),
        Scopes:       scopes,
        RedirectURL:  authConf.RedirectURI,
        AuthStyle:    oauth2.AuthStyleInParams,
    }
    oauthCtx := oidc.ClientContext(context.Background(), &http.Client{})
    otoken, err := conf.Exchange(
        oauthCtx,
        content.Code,
        oauth2.SetAuthURLParam("client_assertion", "foo"),
        oauth2.SetAuthURLParam("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"),
    )
    ...
naizerjohn-ms commented 1 week ago

@zetaab Great point, let me implement this and test... looks like it should work? I'm looking at oauth2/internal/token.go at function 'newTokenRequest', and it seems it only adds the client_secret to the body of the request if it is not null/empty

zetaab commented 1 week ago

@naizerjohn-ms yep it adds it only if its defined, but you can define extra parameters with oauth2.SetAuthURLParam before that. Please test it if you have use-case :)