Replace ThreePID lookups with the new SSO lookups.

This commit is contained in:
Tommie Gannert 2022-05-23 17:55:38 +02:00
parent c3f7945284
commit 03cf5a5c08
3 changed files with 54 additions and 38 deletions

View file

@ -26,7 +26,7 @@ import (
"text/template" "text/template"
"github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/clientapi/userutil" uapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/tidwall/gjson" "github.com/tidwall/gjson"
) )
@ -161,7 +161,7 @@ func (p *baseOIDCIdentityProvider) getOIDCAccessToken(ctx context.Context, req *
return resp.AccessToken, nil return resp.AccessToken, nil
} }
func (p *baseOIDCIdentityProvider) getUserInfo(ctx context.Context, req *IdentityProviderRequest, oidcAccessToken string) (*userutil.ThirdPartyIdentifier, string, error) { func (p *baseOIDCIdentityProvider) getUserInfo(ctx context.Context, req *IdentityProviderRequest, oidcAccessToken string) (ssoUser *UserIdentifier, suggestedUserID string, _ error) {
u, err := p.UserInfoURL.Execute(map[string]interface{}{ u, err := p.UserInfoURL.Execute(map[string]interface{}{
"Config": req.System, "Config": req.System,
}, nil) }, nil)
@ -187,34 +187,44 @@ func (p *baseOIDCIdentityProvider) getUserInfo(ctx context.Context, req *Identit
return nil, "", err return nil, "", err
} }
var email string if ctype != "application/json" {
var suggestedUserID string
switch ctype {
case "application/json":
body, err := ioutil.ReadAll(hresp.Body)
if err != nil {
return nil, "", err
}
emailRes := gjson.GetBytes(body, p.UserInfoEmailPath)
if !emailRes.Exists() {
return nil, "", fmt.Errorf("no email in user info response body")
}
email = emailRes.String()
// This is optional.
userIDRes := gjson.GetBytes(body, p.UserInfoSuggestedUserIDPath)
suggestedUserID = userIDRes.String()
default:
return nil, "", fmt.Errorf("got unknown content type %q for user info", ctype) return nil, "", fmt.Errorf("got unknown content type %q for user info", ctype)
} }
if email == "" { body, err := ioutil.ReadAll(hresp.Body)
return nil, "", fmt.Errorf("no email address in user info") if err != nil {
return nil, "", err
} }
return &userutil.ThirdPartyIdentifier{Medium: "email", Address: email}, suggestedUserID, nil issRes := gjson.GetBytes(body, "iss")
if !issRes.Exists() {
return nil, "", fmt.Errorf("no iss in user info response body")
}
iss := issRes.String()
subRes := gjson.GetBytes(body, "sub")
if !subRes.Exists() {
return nil, "", fmt.Errorf("no sub in user info response body")
}
sub := subRes.String()
if iss == "" {
return nil, "", fmt.Errorf("no iss in user info")
}
if sub == "" {
return nil, "", fmt.Errorf("no sub in user info")
}
// This is optional.
userIDRes := gjson.GetBytes(body, p.UserInfoSuggestedUserIDPath)
suggestedUserID = userIDRes.String()
return &UserIdentifier{
Namespace: uapi.OIDCNamespace,
Issuer: iss,
Subject: sub,
}, suggestedUserID, nil
} }
type urlTemplate struct { type urlTemplate struct {

View file

@ -18,8 +18,8 @@ import (
"context" "context"
"net/url" "net/url"
"github.com/matrix-org/dendrite/clientapi/userutil"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
uapi "github.com/matrix-org/dendrite/userapi/api"
) )
type IdentityProvider interface { type IdentityProvider interface {
@ -37,7 +37,7 @@ type IdentityProviderRequest struct {
type CallbackResult struct { type CallbackResult struct {
RedirectURL string RedirectURL string
Identifier *userutil.ThirdPartyIdentifier Identifier *UserIdentifier
SuggestedUserID string SuggestedUserID string
} }
@ -55,3 +55,8 @@ func GetIdentityProvider(t IdentityProviderType) IdentityProvider {
return nil return nil
} }
} }
type UserIdentifier struct {
Namespace uapi.SSOIssuerNamespace
Issuer, Subject string
}

View file

@ -165,9 +165,9 @@ func SSOCallback(
return util.RedirectResponse(result.RedirectURL) return util.RedirectResponse(result.RedirectURL)
} }
id, err := verifyThirdPartyUserIdentifier(ctx, userAPI, result.Identifier, cfg.Matrix.ServerName) id, err := verifySSOUserIdentifier(ctx, userAPI, result.Identifier, cfg.Matrix.ServerName)
if err != nil { if err != nil {
util.GetLogger(ctx).WithError(err).WithField("identifier", result.Identifier.String()).Error("failed to find user") util.GetLogger(ctx).WithError(err).WithField("identifier", result.Identifier).Error("failed to find user")
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusUnauthorized, Code: http.StatusUnauthorized,
JSON: jsonerror.Forbidden("ID not associated with a local account"), JSON: jsonerror.Forbidden("ID not associated with a local account"),
@ -176,7 +176,7 @@ func SSOCallback(
if id == nil { if id == nil {
// The user doesn't exist. // The user doesn't exist.
// TODO: let the user select a localpart and register an account. // TODO: let the user select a localpart and register an account.
util.GetLogger(ctx).WithError(err).WithField("identifier", result.Identifier.String()).Error("failed to find user") util.GetLogger(ctx).WithError(err).WithField("identifier", result.Identifier).Error("failed to find user")
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusNotImplemented, Code: http.StatusNotImplemented,
JSON: jsonerror.Forbidden("SSO registration not implemented"), JSON: jsonerror.Forbidden("SSO registration not implemented"),
@ -204,7 +204,7 @@ func SSOCallback(
type userAPIForSSO interface { type userAPIForSSO interface {
uapi.LoginTokenInternalAPI uapi.LoginTokenInternalAPI
QueryLocalpartForThreePID(ctx context.Context, req *uapi.QueryLocalpartForThreePIDRequest, res *uapi.QueryLocalpartForThreePIDResponse) error QueryLocalpartForSSO(ctx context.Context, req *uapi.QueryLocalpartForSSORequest, res *uapi.QueryLocalpartForSSOResponse) error
} }
// getProvider looks up the given provider in the // getProvider looks up the given provider in the
@ -254,16 +254,17 @@ func parseNonce(s string) (redirectURL *url.URL, _ error) {
return u, nil return u, nil
} }
// verifyThirdPartyUserIdentifier resolves a ThirdPartyIdentifier to a // verifySSOUserIdentifier resolves an sso.UserIdentifier to a
// UserIdentifier using the User API. Returns nil if there is no // UserIdentifier using the User API. Returns nil if there is no
// associated user. // associated user.
func verifyThirdPartyUserIdentifier(ctx context.Context, userAPI userAPIForSSO, id *userutil.ThirdPartyIdentifier, serverName gomatrixserverlib.ServerName) (*userutil.UserIdentifier, error) { func verifySSOUserIdentifier(ctx context.Context, userAPI userAPIForSSO, id *sso.UserIdentifier, serverName gomatrixserverlib.ServerName) (*userutil.UserIdentifier, error) {
req := &uapi.QueryLocalpartForThreePIDRequest{ req := &uapi.QueryLocalpartForSSORequest{
ThreePID: id.Address, Namespace: id.Namespace,
Medium: string(id.Medium), Issuer: id.Issuer,
Subject: id.Subject,
} }
var res uapi.QueryLocalpartForThreePIDResponse var res uapi.QueryLocalpartForSSOResponse
if err := userAPI.QueryLocalpartForThreePID(ctx, req, &res); err != nil { if err := userAPI.QueryLocalpartForSSO(ctx, req, &res); err != nil {
return nil, err return nil, err
} }
if res.Localpart == "" { if res.Localpart == "" {