Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixes #2478 legacy auth queries missing values #2479

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions controller/internal/routes/current_api_session_api_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ func MapApiSessionAuthQueriesToRestEntity(ae *env.AppEnv, rc *response.RequestCo
MinLength: authQuery.MinLength,
Provider: authQuery.Provider,
TypeID: authQuery.TypeID,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see these fields populated in API session returned from /authenticate?method=cert call

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the same with HA controller: /oidc/login/cert returns

{"authQueries":[{"id":"2u9E63jXaAn7wAvG6vPKWa","provider":"url","scopes":null,"typeId":"EXT-JWT"}]}

Copy link
Member Author

@andrewpmartinez andrewpmartinez Oct 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be odd for legacy, specifically in this PR, because there are tests in this PR that ensure that,

Can you confirm the correct ext-jwt-signer is being configured and used?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also added a set of OIDC value checks, and the values I put in seem to come out.

ClientID: authQuery.ClientID,
Scopes: authQuery.Scopes,
ID: authQuery.ID,
})
}
}
Expand Down
125 changes: 119 additions & 6 deletions tests/auth_external_jwt_signer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,20 @@ func Test_Authenticate_External_Jwt(t *testing.T) {
validJwtSignerCert, validJwtSignerPrivateKey := newSelfSignedCert("valid signer")
validJwtSignerCertPem := nfpem.EncodeToString(validJwtSignerCert)

validJwtSignerAuthUrl := "https://valid.jwt.signer.url.example.com"
validJwtSignerClientId := "valid-client-id"
validJwtSignerScopes := []string{"valid-scope1", "valid-scope2"}

validJwtSigner := &rest_model.ExternalJWTSignerCreate{
CertPem: &validJwtSignerCertPem,
Enabled: B(true),
Name: S("Test JWT Signer - Enabled"),
Kid: S(uuid.NewString()),
Issuer: S("the-very-best-iss"),
Audience: S("the-very-best-aud"),
CertPem: &validJwtSignerCertPem,
Enabled: B(true),
Name: S("Test JWT Signer - Enabled"),
Kid: S(uuid.NewString()),
Issuer: S("the-very-best-iss"),
Audience: S("the-very-best-aud"),
ClientID: &validJwtSignerClientId,
Scopes: validJwtSignerScopes,
ExternalAuthURL: &validJwtSignerAuthUrl,
}

createResponseEnv = &rest_model.CreateEnvelope{}
Expand All @@ -198,6 +205,7 @@ func Test_Authenticate_External_Jwt(t *testing.T) {
ctx.Req.NoError(err)
ctx.Req.Equal(http.StatusCreated, resp.StatusCode())

validSignerUsingInternalId := createResponseEnv.Data.ID
signerIds = append(signerIds, createResponseEnv.Data.ID)

//valid signer w/ external id
Expand Down Expand Up @@ -921,4 +929,109 @@ func Test_Authenticate_External_Jwt(t *testing.T) {
ctx.Req.NotNil(result.Data)
ctx.Req.NotNil(result.Data.Token)
})

t.Run("can authenticate with cert auth and secondary ext-jwt", func(t *testing.T) {
ctx.testContextChanged(t)

//create auth policy, new identity w/ policy, create token for authentication
authPolicyCertExtJwt := &rest_model.AuthPolicyCreate{
Name: ToPtr("createCertJwtAuthPolicy"),
Primary: &rest_model.AuthPolicyPrimary{
Cert: &rest_model.AuthPolicyPrimaryCert{
AllowExpiredCerts: ToPtr(true),
Allowed: ToPtr(true),
},
ExtJWT: &rest_model.AuthPolicyPrimaryExtJWT{
AllowedSigners: []string{},
Allowed: ToPtr(false),
},
Updb: &rest_model.AuthPolicyPrimaryUpdb{
Allowed: ToPtr(false),
LockoutDurationMinutes: ToPtr(int64(0)),
MaxAttempts: ToPtr(int64(3)),
MinPasswordLength: ToPtr(int64(7)),
RequireMixedCase: ToPtr(true),
RequireNumberChar: ToPtr(true),
RequireSpecialChar: ToPtr(true),
},
},
Secondary: &rest_model.AuthPolicySecondary{
RequireExtJWTSigner: ToPtr(validSignerUsingInternalId),
RequireTotp: ToPtr(false),
},
}

createCertJwtAuthPolicy := &rest_model.CreateEnvelope{}
resp, err = ctx.AdminManagementSession.newAuthenticatedRequest().SetResult(createCertJwtAuthPolicy).SetBody(authPolicyCertExtJwt).Post("/auth-policies")
ctx.NoError(err)
ctx.Equal(http.StatusCreated, resp.StatusCode(), string(resp.Body()))
ctx.NotNil(createCertJwtAuthPolicy)
ctx.NotNil(createCertJwtAuthPolicy.Data)
ctx.NotEmpty(createCertJwtAuthPolicy.Data.ID)

newId, certAuth := ctx.AdminManagementSession.requireCreateIdentityOttEnrollment(uuid.NewString(), false)

identityPatch := &rest_model.IdentityPatch{
AuthPolicyID: ToPtr(createCertJwtAuthPolicy.Data.ID),
}

resp, err = ctx.AdminManagementSession.newAuthenticatedRequest().SetBody(identityPatch).Patch("/identities/" + newId)
ctx.NoError(err)
ctx.Equal(http.StatusOK, resp.StatusCode(), string(resp.Body()))

jwtToken := jwt.New(jwt.SigningMethodES256)
jwtToken.Claims = jwt.RegisteredClaims{
Audience: []string{*validJwtSigner.Audience},
ExpiresAt: &jwt.NumericDate{Time: time.Now().Add(2 * time.Hour)},
ID: time.Now().String(),
IssuedAt: &jwt.NumericDate{Time: time.Now()},
Issuer: *validJwtSigner.Issuer,
NotBefore: &jwt.NumericDate{Time: time.Now()},
Subject: newId,
}

jwtToken.Header["kid"] = *validJwtSigner.Kid

jwtStrSigned, err := jwtToken.SignedString(validJwtSignerPrivateKey)
ctx.Req.NoError(err)
ctx.Req.NotEmpty(jwtStrSigned)

t.Run("authenticating with cert and jwt yields 0 auth queries", func(t *testing.T) {
ctx.testContextChanged(t)

result := &rest_model.CurrentAPISessionDetailEnvelope{}

testClient, _, transport := ctx.NewClientComponents(EdgeClientApiPath)

transport.TLSClientConfig.Certificates = certAuth.TLSCertificates()

resp, err := testClient.NewRequest().SetResult(result).SetHeader("Authorization", "Bearer "+jwtStrSigned).Post("/authenticate?method=cert")
ctx.Req.NoError(err)
ctx.Req.Equal(http.StatusOK, resp.StatusCode(), string(resp.Body()))
ctx.Req.Empty(result.Data.AuthQueries)
})

t.Run("authenticating with cert and jwt yields 1 ext-jwt auth query", func(t *testing.T) {
ctx.testContextChanged(t)

result := &rest_model.CurrentAPISessionDetailEnvelope{}

testClient, _, transport := ctx.NewClientComponents(EdgeClientApiPath)

transport.TLSClientConfig.Certificates = certAuth.TLSCertificates()

resp, err := testClient.NewRequest().SetResult(result).Post("/authenticate?method=cert")
ctx.Req.NoError(err)
ctx.Req.Equal(http.StatusOK, resp.StatusCode(), string(resp.Body()))
ctx.Req.Len(result.Data.AuthQueries, 1)

ctx.Req.Equal(rest_model.AuthQueryTypeEXTDashJWT, result.Data.AuthQueries[0].TypeID)
ctx.Req.Equal(validJwtSignerClientId, result.Data.AuthQueries[0].ClientID)
ctx.Req.Equal(validJwtSignerScopes[0], result.Data.AuthQueries[0].Scopes[0])
ctx.Req.Equal(validJwtSignerScopes[1], result.Data.AuthQueries[0].Scopes[1])
ctx.Req.Equal(validJwtSignerAuthUrl, result.Data.AuthQueries[0].HTTPURL)
ctx.Req.Equal(validSignerUsingInternalId, result.Data.AuthQueries[0].ID)

})
})
}
52 changes: 41 additions & 11 deletions tests/auth_oidc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,14 +261,21 @@ func Test_Authenticate_OIDC_Auth(t *testing.T) {
ctx.testContextChanged(t)
jwtSignerCert, _ := newSelfSignedCert("Test Jwt Signer Cert - Auth Policy")

clientId := "test-client-id-99"
scope1 := "test-scope-1-99"
scope2 := "test-scope-2-99"
extAuthUrl := "https://some.auth.url.example.com/auth"
createExtJwtParam := external_jwt_signer.NewCreateExternalJWTSignerParams()
createExtJwtParam.ExternalJWTSigner = &rest_model.ExternalJWTSignerCreate{
CertPem: S(nfpem.EncodeToString(jwtSignerCert)),
Enabled: B(true),
Name: S("Test JWT Signer - Auth Policy"),
Kid: S(uuid.NewString()),
Issuer: S("test-issuer-99"),
Audience: S("test-audience-99"),
CertPem: S(nfpem.EncodeToString(jwtSignerCert)),
Enabled: B(true),
Name: S("Test JWT Signer - Auth Policy"),
Kid: S(uuid.NewString()),
Issuer: S("test-issuer-99"),
Audience: S("test-audience-99"),
ClientID: &clientId,
Scopes: []string{scope1, scope2},
ExternalAuthURL: S(extAuthUrl),
}

extJwtCreateResp, err := managementClient.API.ExternalJWTSigner.CreateExternalJWTSigner(createExtJwtParam, nil)
Expand Down Expand Up @@ -337,7 +344,7 @@ func Test_Authenticate_OIDC_Auth(t *testing.T) {
ctx.Req.NoError(rest_util.WrapErr(err))
ctx.Req.NotNil(createIdentityUpdbAuthenticatorResp)

t.Run("can authenticate via UPDB", func(t *testing.T) {
t.Run("can authenticate via UPDB and see two auth queries", func(t *testing.T) {
ctx.testContextChanged(t)
identityClient := resty.NewWithClient(ctx.NewHttpClient(ctx.NewTransport()))
identityClient.SetRedirectPolicy(resty.DomainCheckRedirectPolicy("127.0.0.1", "localhost"))
Expand All @@ -356,14 +363,37 @@ func Test_Authenticate_OIDC_Auth(t *testing.T) {
ctx.Req.NoError(err)
ctx.Req.Equal(http.StatusOK, resp.StatusCode())

parsedBody := map[string]any{
"authQueries": []*rest_model.AuthQueryDetail{},
type respBody struct {
AuthQueries []*rest_model.AuthQueryDetail `json:"authQueries"`
}

err = json.Unmarshal(resp.Body(), &parsedBody)
parsedBody := &respBody{}

err = json.Unmarshal(resp.Body(), parsedBody)
ctx.Req.NoError(err)

ctx.Req.Len(parsedBody["authQueries"], 2)
ctx.Req.Len(parsedBody.AuthQueries, 2)

extJwtIdx := -1
totpIdx := -1

for i, authQuery := range parsedBody.AuthQueries {
if authQuery.TypeID == rest_model.AuthQueryTypeEXTDashJWT {
extJwtIdx = i
} else if authQuery.TypeID == rest_model.AuthQueryTypeTOTP {
totpIdx = i
} else {
ctx.Req.Failf("unexexpected auth quuery type id encountered: %s", string(authQuery.TypeID))
}
}

ctx.Req.True(extJwtIdx >= 0, "expected extJwtIdx to be set")
ctx.Req.True(totpIdx >= 0, "expected totpIdx to be set")

ctx.Req.Equal(parsedBody.AuthQueries[extJwtIdx].ClientID, clientId)
ctx.Req.Equal(parsedBody.AuthQueries[extJwtIdx].Scopes[0], scope1)
ctx.Req.Equal(parsedBody.AuthQueries[extJwtIdx].Scopes[1], scope2)
ctx.Req.Equal(parsedBody.AuthQueries[extJwtIdx].HTTPURL, extAuthUrl)
})

})
Expand Down
Loading