-
Notifications
You must be signed in to change notification settings - Fork 373
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
153 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,105 @@ | ||
package api | ||
|
||
import ( | ||
"context" | ||
"crypto" | ||
"crypto/rsa" | ||
"crypto/sha256" | ||
"crypto/x509" | ||
"encoding/base64" | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"net/http/httptest" | ||
"net/url" | ||
"time" | ||
|
||
"github.com/coreos/go-oidc/v3/oidc" | ||
jwt "github.com/golang-jwt/jwt" | ||
"github.com/supabase/gotrue/internal/api/provider" | ||
) | ||
|
||
const ( | ||
azureUser string = `{"name":"Azure Test","email":"[email protected]","sub":"azuretestid"}` | ||
azureUserNoEmail string = `{"name":"Azure Test","sub":"azuretestid"}` | ||
) | ||
|
||
func idTokenPrivateKey() *rsa.PrivateKey { | ||
// #nosec | ||
der, err := base64.StdEncoding.DecodeString("MIIEpAIBAAKCAQEAvklrFDsVgbhs3DOQICMqm4xdFoi/MHj/T6XH8S7wXWd0roqdWVarwCLV4y3DILkLre4PzNK+hEY5NAnoAKrsCMyyCb4Wdl8HCdJk4ojDqAig+DJw67imqZoxJMFJyIhfMJhwVK1V8GRUPATn855rygLo7wThahMJeEHNiJr3TtV6Rf35KSs7DuyoWIUSjISYabQozKqIvpdUpTpSqjlOQvjdAxggRyycBZSgLzjWhsA8metnAMO48bX4bgiHLR6Kzu/dfPyEVPfgeYpA2ebIY6GzIUxVS0yX8+ExA6jeLCkuepjLHuz5XCJtd6zzGDXr1eX7nA6ZIeUNdFbWRDnPawIDAQABAoIBABH4Qvl1HvHSJc2hvPGcAJER71SKc2uzcYDnCfu30BEyDO3Sv0tJiQyq/YHnt26mqviw66MPH9jD/PDyIou1mHa4RfPvlJV3IeYGjWprOfbrYbAuq0VHec24dv2el0YtwreHHcyRVfVOtDm6yODTzCAWqEKyNktbIuDNbgiBgetayaJecDRoFMF9TOCeMCL92iZytzAr7fi+JWtLkRS/GZRIBjbr8LJ/ueYoCRmIx3MIw0WdPp7v2ZfeRTxP7LxJZ+MAsrq2pstmZYP7K0305e0bCJX1HexfXLs2Ul7u8zaxrXL8zw4/9+/GMsAeU3ffCVnGz/RKL5+T6iuz2RotjFECgYEA+Xk7DGwRXfDg9xba1GVFGeiC4nybqZw/RfZKcz/RRJWSHRJV/ps1avtbca3B19rjI6rewZMO1NWNv/tI2BdXP8vAKUnI9OHJZ+J/eZzmqDE6qu0v0ddRFUDzCMWE0j8BjrUdy44n4NQgopcv14u0iyr9tuhGO6YXn2SuuvEkZokCgYEAw0PNnT55kpkEhXSp7An2hdBJEub9ST7hS6Kcd8let62/qUZ/t5jWigSkWC1A2bMtH55+LgudIFjiehwVzRs7jym2j4jkKZGonyAX1l9IWgXwKl7Pn49lEQH5Yk6MhnXdyLGoFTzXiUyk/fKvgXX7jow1bD3j6sAc8P495I7TyVMCgYAHg6VJrH+har37805IE3zPWPeIRuSRaUlmnBKGAigVfsPV6FV6w8YKIOQSOn+aNtecnWr0Pa+2rXAFllYNXDaej06Mb9KDvcFJRcM9MIKqEkGIIHjOQ0QH9drcKsbjZk5vs/jfxrpgxULuYstoHKclgff+aGSlK02O2YOB0f2csQKBgQCEC/MdNiWCpKXxFg7fB3HF1i/Eb56zjKlQu7uyKeQ6tG3bLEisQNg8Z5034Apt7gRC0KyluMbeHB2z1BBOLu9dBill8X3SOqVcTpiwKKlF76QVEx622YLQOJSMDXBscYK0+KchDY74U3N0JEzZcI7YPCrYcxYRJy+rLVNvn8LK7wKBgQDE8THsZ589e10F0zDBvPK56o8PJnPeH71sgdM2Co4oLzBJ6g0rpJOKfcc03fLHsoJVOAya9WZeIy6K8+WVdcPTadR07S4p8/tcK1eguu5qlmCUOzswrTKAaJoIHO7cddQp3nySIqgYtkGdHKuvlQDMQkEKJS0meOm+vdeAG2rkaA==") | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
privateKey, err := x509.ParsePKCS1PrivateKey(der) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
return privateKey | ||
} | ||
|
||
func setupAzureOverrideVerifiers() { | ||
provider.OverrideVerifiers[provider.IssuerAzureMicrosoft] = func(ctx context.Context, config *oidc.Config) *oidc.IDTokenVerifier { | ||
pk := idTokenPrivateKey() | ||
|
||
return oidc.NewVerifier( | ||
provider.IssuerAzureMicrosoft, | ||
&oidc.StaticKeySet{ | ||
PublicKeys: []crypto.PublicKey{ | ||
pk.PublicKey, | ||
}, | ||
}, | ||
config, | ||
) | ||
} | ||
} | ||
|
||
func mintIDToken(user string) string { | ||
var idToken struct { | ||
Issuer string `json:"iss"` | ||
IssuedAt int `json:"iat"` | ||
ExpiresAt int `json:"exp"` | ||
Audience string `json:"aud"` | ||
|
||
Sub string `json:"sub,omitempty"` | ||
Name string `json:"name,omitempty"` | ||
Email string `josn:"email,omitempty"` | ||
XmsEdov any `json:"xms_edov,omitempty"` | ||
} | ||
|
||
if err := json.Unmarshal([]byte(user), &idToken); err != nil { | ||
panic(err) | ||
} | ||
|
||
now := time.Now() | ||
|
||
idToken.Issuer = provider.IssuerAzureMicrosoft | ||
idToken.IssuedAt = int(now.Unix()) | ||
idToken.ExpiresAt = int(now.Unix() + 60*60) | ||
idToken.Audience = "testclientid" | ||
|
||
header := base64.RawURLEncoding.EncodeToString([]byte(`{"typ":"JWT","alg":"RS256"}`)) | ||
|
||
data, err := json.Marshal(idToken) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
payload := base64.RawURLEncoding.EncodeToString(data) | ||
sum := sha256.Sum256([]byte(header + "." + payload)) | ||
|
||
pk := idTokenPrivateKey() | ||
sig, err := rsa.SignPKCS1v15(nil, pk, 0, sum[:]) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
token := header + "." + payload + "." + base64.RawURLEncoding.EncodeToString(sig) | ||
|
||
return token | ||
} | ||
|
||
func (ts *ExternalTestSuite) TestSignupExternalAzure() { | ||
req := httptest.NewRequest(http.MethodGet, "http://localhost/authorize?provider=azure", nil) | ||
w := httptest.NewRecorder() | ||
|
@@ -48,11 +134,7 @@ func AzureTestSignupSetup(ts *ExternalTestSuite, tokenCount *int, userCount *int | |
ts.Equal(ts.Config.External.Azure.RedirectURI, r.FormValue("redirect_uri")) | ||
|
||
w.Header().Add("Content-Type", "application/json") | ||
fmt.Fprint(w, `{"access_token":"azure_token","expires_in":100000}`) | ||
case "/oidc/userinfo": | ||
*userCount++ | ||
w.Header().Add("Content-Type", "application/json") | ||
fmt.Fprint(w, user) | ||
fmt.Fprintf(w, `{"access_token":"azure_token","expires_in":100000,"id_token":%q}`, mintIDToken(user)) | ||
default: | ||
w.WriteHeader(500) | ||
ts.Fail("unknown azure oauth call %s", r.URL.Path) | ||
|
@@ -66,6 +148,8 @@ func AzureTestSignupSetup(ts *ExternalTestSuite, tokenCount *int, userCount *int | |
} | ||
|
||
func (ts *ExternalTestSuite) TestSignupExternalAzure_AuthorizationCode() { | ||
setupAzureOverrideVerifiers() | ||
|
||
ts.Config.DisableSignup = false | ||
tokenCount, userCount := 0, 0 | ||
code := "authcode" | ||
|
@@ -78,6 +162,8 @@ func (ts *ExternalTestSuite) TestSignupExternalAzure_AuthorizationCode() { | |
} | ||
|
||
func (ts *ExternalTestSuite) TestSignupExternalAzureDisableSignupErrorWhenNoUser() { | ||
setupAzureOverrideVerifiers() | ||
|
||
ts.Config.DisableSignup = true | ||
tokenCount, userCount := 0, 0 | ||
code := "authcode" | ||
|
@@ -90,6 +176,8 @@ func (ts *ExternalTestSuite) TestSignupExternalAzureDisableSignupErrorWhenNoUser | |
} | ||
|
||
func (ts *ExternalTestSuite) TestSignupExternalAzureDisableSignupErrorWhenNoEmail() { | ||
setupAzureOverrideVerifiers() | ||
|
||
ts.Config.DisableSignup = true | ||
tokenCount, userCount := 0, 0 | ||
code := "authcode" | ||
|
@@ -99,10 +187,11 @@ func (ts *ExternalTestSuite) TestSignupExternalAzureDisableSignupErrorWhenNoEmai | |
u := performAuthorization(ts, "azure", code, "") | ||
|
||
assertAuthorizationFailure(ts, u, "Error getting user email from external provider", "server_error", "[email protected]") | ||
|
||
} | ||
|
||
func (ts *ExternalTestSuite) TestSignupExternalAzureDisableSignupSuccessWithPrimaryEmail() { | ||
setupAzureOverrideVerifiers() | ||
|
||
ts.Config.DisableSignup = true | ||
|
||
ts.createUser("azuretestid", "[email protected]", "Azure Test", "http://example.com/avatar", "") | ||
|
@@ -118,6 +207,8 @@ func (ts *ExternalTestSuite) TestSignupExternalAzureDisableSignupSuccessWithPrim | |
} | ||
|
||
func (ts *ExternalTestSuite) TestInviteTokenExternalAzureSuccessWhenMatchingToken() { | ||
setupAzureOverrideVerifiers() | ||
|
||
// name should be populated from Azure API | ||
ts.createUser("azuretestid", "[email protected]", "", "http://example.com/avatar", "invite_token") | ||
|
||
|
@@ -132,6 +223,8 @@ func (ts *ExternalTestSuite) TestInviteTokenExternalAzureSuccessWhenMatchingToke | |
} | ||
|
||
func (ts *ExternalTestSuite) TestInviteTokenExternalAzureErrorWhenNoMatchingToken() { | ||
setupAzureOverrideVerifiers() | ||
|
||
tokenCount, userCount := 0, 0 | ||
code := "authcode" | ||
azureUser := `{"name":"Azure Test","avatar":{"href":"http://example.com/avatar"}}` | ||
|
@@ -143,6 +236,8 @@ func (ts *ExternalTestSuite) TestInviteTokenExternalAzureErrorWhenNoMatchingToke | |
} | ||
|
||
func (ts *ExternalTestSuite) TestInviteTokenExternalAzureErrorWhenWrongToken() { | ||
setupAzureOverrideVerifiers() | ||
|
||
ts.createUser("azuretestid", "[email protected]", "", "", "invite_token") | ||
|
||
tokenCount, userCount := 0, 0 | ||
|
@@ -156,6 +251,8 @@ func (ts *ExternalTestSuite) TestInviteTokenExternalAzureErrorWhenWrongToken() { | |
} | ||
|
||
func (ts *ExternalTestSuite) TestInviteTokenExternalAzureErrorWhenEmailDoesntMatch() { | ||
setupAzureOverrideVerifiers() | ||
|
||
ts.createUser("azuretestid", "[email protected]", "", "", "invite_token") | ||
|
||
tokenCount, userCount := 0, 0 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters