Skip to content

Commit

Permalink
fix azure tenant login
Browse files Browse the repository at this point in the history
  • Loading branch information
alexleetc28 authored Mar 17, 2024
1 parent 5c04104 commit 8e82b5b
Showing 1 changed file with 83 additions and 26 deletions.
109 changes: 83 additions & 26 deletions internal/api/provider/azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const IssuerAzureMicrosoft = "https://login.microsoftonline.com/9188040d-6c67-4c

const (
defaultAzureAuthBase = "login.microsoftonline.com/common"
defaultAzureAPIBase = "graph.microsoft.com"
)

type azureProvider struct {
Expand All @@ -36,6 +37,14 @@ type azureProvider struct {
// ID token received is issued by that specific issuer, and so
// ExpectedIssuer contains the issuer URL of that tenant.
ExpectedIssuer string
APIPath string
}

type azureUser struct {
Name string `json:"name"`
Email string `json:"email"`
Sub string `json:"sub"`
OtherMails []string `json:"otherMails"`
}

var azureIssuerRegexp = regexp.MustCompile("^https://login[.]microsoftonline[.]com/([^/]+)/v2[.]0/?$")
Expand All @@ -57,6 +66,7 @@ func NewAzureProvider(ext conf.OAuthProviderConfiguration, scopes string) (OAuth
}

authHost := chooseHost(ext.URL, defaultAzureAuthBase)
apiPath := chooseHost(ext.ApiURL, defaultAzureAPIBase)
expectedIssuer := ""

if ext.URL != "" {
Expand All @@ -83,6 +93,7 @@ func NewAzureProvider(ext conf.OAuthProviderConfiguration, scopes string) (OAuth
Scopes: oauthScopes,
},
ExpectedIssuer: expectedIssuer,
APIPath: apiPath,
}, nil
}

Expand Down Expand Up @@ -113,44 +124,90 @@ func DetectAzureIDTokenIssuer(ctx context.Context, idToken string) (string, erro
}

func (g azureProvider) GetUserData(ctx context.Context, tok *oauth2.Token) (*UserProvidedData, error) {
idToken := tok.Extra("id_token")
if g.APIPath == defaultAzureAPIBase {
idToken := tok.Extra("id_token")

if idToken != nil {
issuer, err := DetectAzureIDTokenIssuer(ctx, idToken.(string))
if err != nil {
return nil, err
}

if !IsAzureIssuer(issuer) {
return nil, fmt.Errorf("azure: ID token issuer not valid %q", issuer)
}

if g.ExpectedIssuer != "" && issuer != g.ExpectedIssuer {
// Since ExpectedIssuer was set, then the developer had
// setup GoTrue to use the tenant-specific
// authorization endpoint, which in-turn means that
// only those tenant's ID tokens will be accepted.
return nil, fmt.Errorf("azure: ID token issuer %q does not match expected issuer %q", issuer, g.ExpectedIssuer)
}

provider, err := oidc.NewProvider(ctx, issuer)
if err != nil {
return nil, err
}

_, data, err := ParseIDToken(ctx, provider, &oidc.Config{
ClientID: g.ClientID,
}, idToken.(string), ParseIDTokenOptions{
AccessToken: tok.AccessToken,
})
if err != nil {
return nil, err
}

return data, nil
}

if idToken != nil {
issuer, err := DetectAzureIDTokenIssuer(ctx, idToken.(string))
if err != nil {
return nil, fmt.Errorf("azure: no OIDC ID token present in response")
} else {
var u azureUser
if err := makeRequest(ctx, tok, g.Config, g.APIPath+"/oidc/userinfo", &u); err != nil {
return nil, err
}

if !IsAzureIssuer(issuer) {
return nil, fmt.Errorf("azure: ID token issuer not valid %q", issuer)
var data UserProvidedData

data.Metadata = &Claims{
Issuer: g.APIPath,
Subject: u.Sub,
Name: u.Name,

// To be deprecated
FullName: u.Name,
ProviderId: u.Sub,
}

if g.ExpectedIssuer != "" && issuer != g.ExpectedIssuer {
// Since ExpectedIssuer was set, then the developer had
// setup GoTrue to use the tenant-specific
// authorization endpoint, which in-turn means that
// only those tenant's ID tokens will be accepted.
return nil, fmt.Errorf("azure: ID token issuer %q does not match expected issuer %q", issuer, g.ExpectedIssuer)
if u.Email != "" {
data.Emails = append(data.Emails, Email{
Email: u.Email,
Verified: true,
})
}

provider, err := oidc.NewProvider(ctx, issuer)
if err != nil {
return nil, err
if u.OtherMails != nil {
for _, mail := range u.OtherMails {
if mail != "" {
data.Emails = append(data.Emails, Email{
Email: mail,
Verified: false,
})
}
}
}

_, data, err := ParseIDToken(ctx, provider, &oidc.Config{
ClientID: g.ClientID,
}, idToken.(string), ParseIDTokenOptions{
AccessToken: tok.AccessToken,
})
if err != nil {
return nil, err
if len(data.Emails) == 0 {
return nil, fmt.Errorf("unable to find email with Azure provider")
}

return data, nil
}
data.Emails[0].Primary = true

// Only ID tokens supported, UserInfo endpoint has a history of being less secure.
data.Metadata.Email = data.Emails[0].Email
data.Metadata.EmailVerified = data.Emails[0].Verified

return nil, fmt.Errorf("azure: no OIDC ID token present in response")
return &data, nil
}
}

0 comments on commit 8e82b5b

Please sign in to comment.