volatiletech / authboss

The boss of http auth.
MIT License
3.79k stars 204 forks source link

OpenID Connect (oidc) support #332

Open oliverpool opened 2 years ago

oliverpool commented 2 years ago

OpenID Connect seems like an easier way to offer login than oauth2.

If you think that this is a good idea, I could try to add it as a subpackage github.com/volatiletech/authboss/v3/oauth2/oidc.

Here is my current implementation (based on https://github.com/caos/oidc):

package oidc

import (
    "context"
    "fmt"
    "net/http"
    "time"

    "github.com/caos/oidc/pkg/client"
    httphelper "github.com/caos/oidc/pkg/http"
    caosoidc "github.com/caos/oidc/pkg/oidc"
    "github.com/volatiletech/authboss/v3"
    aboauth2 "github.com/volatiletech/authboss/v3/oauth2"
    "golang.org/x/oauth2"
)

// NewProvider discovers the configuration of the OpenID Connect provider, based on issuer.
func NewProvider(issuer, clientID, clientSecret string, scopes []string) (authboss.OAuth2Provider, error) {
    cfg := &oauth2.Config{
        ClientID:     clientID,
        ClientSecret: clientSecret,
        RedirectURL:  "", // will be filled later during init
        Scopes:       scopes,
    }
    httpClient := &http.Client{
        Timeout: 30 * time.Second,
    }

    dicovered, err := client.Discover(issuer, httpClient)
    if err != nil {
        return authboss.OAuth2Provider{}, err
    }
    cfg.Endpoint = oauth2.Endpoint{
        AuthURL:   dicovered.AuthorizationEndpoint,
        TokenURL:  dicovered.TokenEndpoint,
    }

    return authboss.OAuth2Provider{
        OAuth2Config: cfg,
        FindUserDetails: func(ctx context.Context, cfg oauth2.Config, token *oauth2.Token) (map[string]string, error) {
            req, err := http.NewRequest("GET", dicovered.UserinfoEndpoint, nil)
            if err != nil {
                return nil, err
            }
            req.Header.Set("authorization", token.TokenType+" "+token.AccessToken)
            userinfo := caosoidc.NewUserInfo()
            if err := httphelper.HttpRequest(httpClient, req, &userinfo); err != nil {
                return nil, err
            }

            user := map[string]string{
                aboauth2.OAuth2UID: userinfo.GetSubject(),
            }
            if email := userinfo.GetEmail(); email != "" {
                user[aboauth2.OAuth2Email] = email
            }

            return user, nil
        },
    }, nil
}
aarondl commented 2 years ago

If you want to champion this then I'm all for it. Just improves Authboss in general so I think that's great. I don't have any experience with OpenID Connect but it does seem quite popular.

oliverpool commented 2 years ago

I have created an external module to experiment on this: https://pkg.go.dev/git.sr.ht/~oliverpool/goath

It seems to work fine in my case, but I would really appreciate a review!

import "git.sr.ht/~oliverpool/goath/goauthboss"

// ...

    ab = authboss.New()
    goauthboss.Init(ab, map[string]goath.Provider{
        "oidc": &coreos.OIDC{...},
    }, store)

In there, I also did the cleanup that I suggested here: https://github.com/volatiletech/authboss/issues/333#issuecomment-1034989236