39272 Check entra tenant ID (#39780)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #39272 Changes file already added on another subtask # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements) - [x] If paths of existing endpoints are modified without backwards compatibility, checked the frontend/CLI for any necessary changes ## Testing - [x] Added/updated automated tests - [x] Where appropriate, [automated tests simulate multiple hosts and test for host isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing) (updates to one hosts's records do not affect another) - [x] QA'd all new/changed functionality manually
This commit is contained in:
@@ -50,6 +50,8 @@ type TestWindowsMDMClient struct {
|
||||
jwtSigningKey *rsa.PrivateKey
|
||||
// jwtSigningKeyID is the ID to report in the header for the signing key
|
||||
jwtSigningKeyID string
|
||||
// Entra Tenant ID to include int he JWT
|
||||
entraTenantID string
|
||||
|
||||
username string
|
||||
password string
|
||||
@@ -77,10 +79,11 @@ func TestWindowsMDMClientNotInOOBE() TestWindowsMDMClientOption {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWindowsMDMClientWithSigningKey(signingKey *rsa.PrivateKey, signingKeyID string) TestWindowsMDMClientOption {
|
||||
func TestWindowsMDMClientWithSigningKeyAndTenantID(signingKey *rsa.PrivateKey, signingKeyID, tenantID string) TestWindowsMDMClientOption {
|
||||
return func(c *TestWindowsMDMClient) {
|
||||
c.jwtSigningKey = signingKey
|
||||
c.jwtSigningKeyID = signingKeyID
|
||||
c.entraTenantID = tenantID
|
||||
}
|
||||
}
|
||||
|
||||
@@ -687,9 +690,11 @@ func (c *TestWindowsMDMClient) getToken() (binarySecToken string, tokenValueType
|
||||
case fleet.WindowsMDMAutomaticEnrollmentType:
|
||||
claims := &jwt.MapClaims{
|
||||
"upn": c.TokenIdentifier,
|
||||
"tid": "tenant_id",
|
||||
"tid": c.entraTenantID,
|
||||
"unique_name": "foo_bar",
|
||||
"scp": "mdm_delegation",
|
||||
"iss": "https://sts.windows.net/" + c.entraTenantID + "/",
|
||||
"aud": c.fleetServerURL,
|
||||
}
|
||||
if c.jwtSigningKey == nil || c.jwtSigningKeyID == "" {
|
||||
return "", "", errors.New("jwt signing key is not set")
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/fleetdm/fleet/v4/server/mdm/microsoft/syncml"
|
||||
"github.com/fleetdm/fleet/v4/server/mdm/nanomdm/cryptoutil"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/google/uuid"
|
||||
"github.com/smallstep/pkcs7"
|
||||
)
|
||||
|
||||
@@ -67,6 +68,7 @@ type STSClaims struct {
|
||||
|
||||
type AzureData struct {
|
||||
UPN string
|
||||
Audience []string
|
||||
TenantID string
|
||||
UniqueName string
|
||||
SCP string
|
||||
@@ -320,6 +322,40 @@ func GetAzureAuthTokenClaims(ctx context.Context, tokenStr string) (AzureData, e
|
||||
return AzureData{}, ctxerr.New(ctx, "invalid TenantID claim")
|
||||
}
|
||||
|
||||
// Validate that tenant ID is a UUID and matches the issuer
|
||||
_, err = uuid.Parse(tenantIDClaim)
|
||||
if err != nil {
|
||||
return AzureData{}, ctxerr.Wrap(ctx, err, "invalid TenantID claim format")
|
||||
}
|
||||
issuer, ok := claims["iss"].(string)
|
||||
if !ok || len(issuer) == 0 {
|
||||
return AzureData{}, ctxerr.New(ctx, "invalid Issuer claim")
|
||||
}
|
||||
|
||||
// Depending on exactly how the Azure AD app is configured, the issuer claim
|
||||
// may vary. Validate that the issuer contains the tenant ID.
|
||||
issuerMatchesTenant := false
|
||||
for _, expectedIssuer := range []string{fmt.Sprintf("https://sts.windows.net/%s/", tenantIDClaim), fmt.Sprintf("https://login.microsoftonline.com/%s/", tenantIDClaim)} {
|
||||
if strings.HasPrefix(issuer, expectedIssuer) {
|
||||
issuerMatchesTenant = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !issuerMatchesTenant {
|
||||
return AzureData{}, ctxerr.New(ctx, "issuer claim does not match tenant ID")
|
||||
}
|
||||
|
||||
audience := []string{}
|
||||
singleAudience, ok := claims["aud"].(string)
|
||||
if !ok {
|
||||
multiAudience, ok := claims["aud"].([]string)
|
||||
if ok {
|
||||
audience = multiAudience
|
||||
}
|
||||
} else {
|
||||
audience = append(audience, singleAudience)
|
||||
}
|
||||
|
||||
// Get UniqueName claim
|
||||
uniqueNameClaim, ok := claims["unique_name"].(string)
|
||||
if !ok {
|
||||
@@ -337,6 +373,7 @@ func GetAzureAuthTokenClaims(ctx context.Context, tokenStr string) (AzureData, e
|
||||
TenantID: tenantIDClaim,
|
||||
UniqueName: uniqueNameClaim,
|
||||
SCP: azureSCPClaim,
|
||||
Audience: audience,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -458,13 +458,18 @@ func (s *integrationMDMTestSuite) TestTurnOnLifecycleEventsWindows() {
|
||||
t.Skip("wipe tests are not supported for windows automatic enrollment until we fix #TODO")
|
||||
}
|
||||
|
||||
tenantID := uuid.New().String()
|
||||
|
||||
acResp := appConfigResponse{}
|
||||
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ "mdm": { "windows_entra_tenant_ids": ["`+tenantID+`"] } }`), http.StatusOK, &acResp)
|
||||
|
||||
err := s.ds.ApplyEnrollSecrets(context.Background(), nil, []*fleet.EnrollSecret{{Secret: t.Name()}})
|
||||
require.NoError(t, err)
|
||||
|
||||
host := createOrbitEnrolledHost(t, "windows", "windows_automatic", s.ds)
|
||||
|
||||
azureMail := "foo.bar.baz@example.com"
|
||||
device := mdmtest.NewTestMDMClientWindowsAutomatic(s.server.URL, azureMail, mdmtest.TestWindowsMDMClientWithSigningKey(s.jwtSigningKey, defaultFakeJWTKeyID))
|
||||
device := mdmtest.NewTestMDMClientWindowsAutomatic(s.server.URL, azureMail, mdmtest.TestWindowsMDMClientWithSigningKeyAndTenantID(s.jwtSigningKey, defaultFakeJWTKeyID, tenantID))
|
||||
device.HardwareID = host.UUID
|
||||
device.DeviceID = host.UUID
|
||||
require.NoError(t, device.Enroll())
|
||||
|
||||
@@ -1610,23 +1610,23 @@ func enrollWindowsHostInMDMViaOrbit(t *testing.T, host *fleet.Host, ds fleet.Dat
|
||||
}
|
||||
|
||||
// Simulates a host being orbit enrolled first then an MDM enrollment coming via the settings app
|
||||
func (s *integrationMDMTestSuite) createWindowsHostThenEnrollMDMViaSettingsApp(fleetServerURL, email string) (*fleet.Host, *mdmtest.TestWindowsMDMClient) {
|
||||
func (s *integrationMDMTestSuite) createWindowsHostThenEnrollMDMViaSettingsApp(fleetServerURL, email, tenantID string) (*fleet.Host, *mdmtest.TestWindowsMDMClient) {
|
||||
host := createOrbitEnrolledHost(s.T(), "windows", uuid.NewString(), s.ds)
|
||||
mdmDevice := s.enrollWindowsMDMViaSettingsApp(fleetServerURL, email)
|
||||
mdmDevice := s.enrollWindowsMDMViaSettingsApp(fleetServerURL, email, tenantID)
|
||||
return host, mdmDevice
|
||||
}
|
||||
|
||||
// Note that this method only creates the MDM Enrollment but it will still need to be linked to the host record either
|
||||
// via DS methods or by simualting a refetch.
|
||||
func (s *integrationMDMTestSuite) enrollWindowsMDMViaSettingsApp(fleetServerURL, email string) *mdmtest.TestWindowsMDMClient {
|
||||
mdmDevice := mdmtest.NewTestMDMClientWindowsAutomatic(fleetServerURL, email, mdmtest.TestWindowsMDMClientNotInOOBE(), mdmtest.TestWindowsMDMClientWithSigningKey(s.jwtSigningKey, defaultFakeJWTKeyID))
|
||||
func (s *integrationMDMTestSuite) enrollWindowsMDMViaSettingsApp(fleetServerURL, email, tenantID string) *mdmtest.TestWindowsMDMClient {
|
||||
mdmDevice := mdmtest.NewTestMDMClientWindowsAutomatic(fleetServerURL, email, mdmtest.TestWindowsMDMClientNotInOOBE(), mdmtest.TestWindowsMDMClientWithSigningKeyAndTenantID(s.jwtSigningKey, defaultFakeJWTKeyID, tenantID))
|
||||
err := mdmDevice.Enroll()
|
||||
require.NoError(s.T(), err)
|
||||
return mdmDevice
|
||||
}
|
||||
|
||||
func (s *integrationMDMTestSuite) enrollWindowsHostInMDMViaAutopilot(fleetServerURL, email string) *mdmtest.TestWindowsMDMClient {
|
||||
mdmDevice := mdmtest.NewTestMDMClientWindowsAutomatic(fleetServerURL, email, mdmtest.TestWindowsMDMClientWithSigningKey(s.jwtSigningKey, defaultFakeJWTKeyID))
|
||||
func (s *integrationMDMTestSuite) enrollWindowsHostInMDMViaAutopilot(fleetServerURL, email, tenantID string) *mdmtest.TestWindowsMDMClient {
|
||||
mdmDevice := mdmtest.NewTestMDMClientWindowsAutomatic(fleetServerURL, email, mdmtest.TestWindowsMDMClientWithSigningKeyAndTenantID(s.jwtSigningKey, defaultFakeJWTKeyID, tenantID))
|
||||
err := mdmDevice.Enroll()
|
||||
require.NoError(s.T(), err)
|
||||
return mdmDevice
|
||||
@@ -7944,13 +7944,20 @@ func (s *integrationMDMTestSuite) TestValidGetPoliciesRequestWithDeviceToken() {
|
||||
func (s *integrationMDMTestSuite) TestValidGetPoliciesRequestWithAzureToken() {
|
||||
t := s.T()
|
||||
|
||||
tenantID := uuid.New().String()
|
||||
|
||||
acResp := appConfigResponse{}
|
||||
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ "mdm": { "windows_entra_tenant_ids": ["`+tenantID+`"] } }`), http.StatusOK, &acResp)
|
||||
|
||||
// Preparing the GetPolicies Request message with Azure JWT token
|
||||
// Preparing the SecurityToken Request message with Azure JWT token
|
||||
claims := &jwt.MapClaims{
|
||||
"upn": "fleetie@example.com",
|
||||
"tid": "tenant_id",
|
||||
"tid": tenantID,
|
||||
"iss": "https://sts.windows.net/" + tenantID + "/",
|
||||
"unique_name": "foo_bar",
|
||||
"scp": "mdm_delegation",
|
||||
"aud": s.server.URL + microsoft_mdm.MDE2PolicyPath,
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
||||
@@ -8120,12 +8127,19 @@ func (s *integrationMDMTestSuite) TestValidRequestSecurityTokenRequestWithDevice
|
||||
func (s *integrationMDMTestSuite) TestValidRequestSecurityTokenRequestWithAzureToken() {
|
||||
t := s.T()
|
||||
|
||||
tenantID := uuid.New().String()
|
||||
|
||||
acResp := appConfigResponse{}
|
||||
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ "mdm": { "windows_entra_tenant_ids": ["`+tenantID+`"] } }`), http.StatusOK, &acResp)
|
||||
|
||||
// Preparing the SecurityToken Request message with Azure JWT token
|
||||
claims := &jwt.MapClaims{
|
||||
"upn": "fleetie@example.com",
|
||||
"tid": "tenant_id",
|
||||
"tid": tenantID,
|
||||
"iss": "https://sts.windows.net/" + tenantID + "/",
|
||||
"unique_name": "foo_bar",
|
||||
"scp": "mdm_delegation",
|
||||
"aud": s.server.URL,
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
||||
@@ -8702,8 +8716,12 @@ func (s *integrationMDMTestSuite) TestWindowsAutomaticEnrollmentCommands() {
|
||||
err := s.ds.ApplyEnrollSecrets(ctx, nil, []*fleet.EnrollSecret{{Secret: t.Name()}})
|
||||
require.NoError(t, err)
|
||||
|
||||
tenantID := uuid.New().String()
|
||||
acResp := appConfigResponse{}
|
||||
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ "mdm": { "windows_entra_tenant_ids": ["`+tenantID+`"] } }`), http.StatusOK, &acResp)
|
||||
|
||||
azureMail := "foo.bar.baz@example.com"
|
||||
d := mdmtest.NewTestMDMClientWindowsAutomatic(s.server.URL, azureMail, mdmtest.TestWindowsMDMClientWithSigningKey(s.jwtSigningKey, defaultFakeJWTKeyID))
|
||||
d := mdmtest.NewTestMDMClientWindowsAutomatic(s.server.URL, azureMail, mdmtest.TestWindowsMDMClientWithSigningKeyAndTenantID(s.jwtSigningKey, defaultFakeJWTKeyID, tenantID))
|
||||
require.NoError(t, d.Enroll())
|
||||
|
||||
checkinAndAck := func(expectFleetdCmds bool) {
|
||||
@@ -8806,20 +8824,49 @@ func (s *integrationMDMTestSuite) TestWindowsAzureInitiatedBadKeys() {
|
||||
_, badKey, err := apple_mdm.NewSCEPCACertKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
tenantID := uuid.New().String()
|
||||
acResp := appConfigResponse{}
|
||||
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ "mdm": { "windows_entra_tenant_ids": ["`+tenantID+`"] } }`), http.StatusOK, &acResp)
|
||||
|
||||
autopilotUserMail := "swan@example.com"
|
||||
|
||||
// Bad key
|
||||
mdmDevice := mdmtest.NewTestMDMClientWindowsAutomatic(s.server.URL, autopilotUserMail, mdmtest.TestWindowsMDMClientWithSigningKey(badKey, defaultFakeJWTKeyID))
|
||||
mdmDevice := mdmtest.NewTestMDMClientWindowsAutomatic(s.server.URL, autopilotUserMail, mdmtest.TestWindowsMDMClientWithSigningKeyAndTenantID(badKey, defaultFakeJWTKeyID, tenantID))
|
||||
err = mdmDevice.Enroll()
|
||||
require.Error(s.T(), err)
|
||||
|
||||
// Good key but wrong ID
|
||||
mdmDevice = mdmtest.NewTestMDMClientWindowsAutomatic(s.server.URL, autopilotUserMail, mdmtest.TestWindowsMDMClientWithSigningKey(s.jwtSigningKey, "bad-key-id"))
|
||||
mdmDevice = mdmtest.NewTestMDMClientWindowsAutomatic(s.server.URL, autopilotUserMail, mdmtest.TestWindowsMDMClientWithSigningKeyAndTenantID(s.jwtSigningKey, "bad-key-id", tenantID))
|
||||
err = mdmDevice.Enroll()
|
||||
require.Error(s.T(), err)
|
||||
|
||||
// Happy path to ensure the setup is correct
|
||||
mdmDevice = mdmtest.NewTestMDMClientWindowsAutomatic(s.server.URL, autopilotUserMail, mdmtest.TestWindowsMDMClientWithSigningKey(s.jwtSigningKey, defaultFakeJWTKeyID))
|
||||
mdmDevice = mdmtest.NewTestMDMClientWindowsAutomatic(s.server.URL, autopilotUserMail, mdmtest.TestWindowsMDMClientWithSigningKeyAndTenantID(s.jwtSigningKey, defaultFakeJWTKeyID, tenantID))
|
||||
err = mdmDevice.Enroll()
|
||||
require.NoError(s.T(), err)
|
||||
}
|
||||
|
||||
func (s *integrationMDMTestSuite) TestWindowsAzureInitiatedTenantIDs() {
|
||||
t := s.T()
|
||||
ctx := context.Background()
|
||||
|
||||
// define a global enroll secret
|
||||
err := s.ds.ApplyEnrollSecrets(ctx, nil, []*fleet.EnrollSecret{{Secret: t.Name()}})
|
||||
require.NoError(t, err)
|
||||
|
||||
tenantID := uuid.New().String()
|
||||
acResp := appConfigResponse{}
|
||||
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ "mdm": { "windows_entra_tenant_ids": ["`+tenantID+`"] } }`), http.StatusOK, &acResp)
|
||||
|
||||
autopilotUserMail := "swan@example.com"
|
||||
|
||||
// Bad entra tenant ID
|
||||
mdmDevice := mdmtest.NewTestMDMClientWindowsAutomatic(s.server.URL, autopilotUserMail, mdmtest.TestWindowsMDMClientWithSigningKeyAndTenantID(s.jwtSigningKey, defaultFakeJWTKeyID, uuid.New().String()))
|
||||
err = mdmDevice.Enroll()
|
||||
require.Error(s.T(), err)
|
||||
|
||||
// Happy path to ensure the setup is correct
|
||||
mdmDevice = mdmtest.NewTestMDMClientWindowsAutomatic(s.server.URL, autopilotUserMail, mdmtest.TestWindowsMDMClientWithSigningKeyAndTenantID(s.jwtSigningKey, defaultFakeJWTKeyID, tenantID))
|
||||
err = mdmDevice.Enroll()
|
||||
require.NoError(s.T(), err)
|
||||
}
|
||||
@@ -8832,6 +8879,10 @@ func (s *integrationMDMTestSuite) TestWindowsAzureInitiatedEnrollmentAndMapping(
|
||||
err := s.ds.ApplyEnrollSecrets(ctx, nil, []*fleet.EnrollSecret{{Secret: t.Name()}})
|
||||
require.NoError(t, err)
|
||||
|
||||
tenantID := uuid.New().String()
|
||||
acResp := appConfigResponse{}
|
||||
s.DoJSON("PATCH", "/api/latest/fleet/config", json.RawMessage(`{ "mdm": { "windows_entra_tenant_ids": ["`+tenantID+`"] } }`), http.StatusOK, &acResp)
|
||||
|
||||
team, err := s.ds.NewTeam(context.Background(), &fleet.Team{
|
||||
Name: "team1_" + t.Name(),
|
||||
Description: "desc team1_" + t.Name(),
|
||||
@@ -8840,11 +8891,11 @@ func (s *integrationMDMTestSuite) TestWindowsAzureInitiatedEnrollmentAndMapping(
|
||||
|
||||
// Enroll another host to ensure the wires don't get crossed somehow
|
||||
autopilotUserMail := "swan@example.com"
|
||||
autopilotDevice := s.enrollWindowsHostInMDMViaAutopilot(s.server.URL, autopilotUserMail)
|
||||
autopilotDevice := s.enrollWindowsHostInMDMViaAutopilot(s.server.URL, autopilotUserMail, tenantID)
|
||||
require.NoError(t, autopilotDevice.Enroll())
|
||||
|
||||
settingsAppUserMail := "fleetie@example.com"
|
||||
settingsAppHost, settingsAppDevice := s.createWindowsHostThenEnrollMDMViaSettingsApp(s.server.URL, settingsAppUserMail)
|
||||
settingsAppHost, settingsAppDevice := s.createWindowsHostThenEnrollMDMViaSettingsApp(s.server.URL, settingsAppUserMail, tenantID)
|
||||
require.NoError(t, settingsAppDevice.Enroll())
|
||||
|
||||
// Transfer the host to the team. Ensure it doesn't wind up in "No team" at the end
|
||||
|
||||
@@ -1007,7 +1007,6 @@ func (svc *Service) authBinarySecurityToken(ctx context.Context, authToken *flee
|
||||
|
||||
// Validating the Binary Security Token Type used on Automatic Enrollments (returned by STS Auth Endpoint)
|
||||
if binSecToken.Type == mdm_types.WindowsMDMAutomaticEnrollmentType {
|
||||
|
||||
upnToken, err := svc.wstepCertManager.GetSTSAuthTokenUPNClaim(binSecToken.Payload.AuthToken)
|
||||
if err != nil {
|
||||
return "", "", ctxerr.Wrap(ctx, err, "issue retrieving UPN from Auth token")
|
||||
@@ -1020,6 +1019,20 @@ func (svc *Service) authBinarySecurityToken(ctx context.Context, authToken *flee
|
||||
|
||||
// Validating the Binary Security Token Type used on Automatic Enrollments
|
||||
if authToken.IsAzureJWTToken() {
|
||||
appConfig, err := svc.ds.AppConfig(ctx)
|
||||
if err != nil {
|
||||
return "", "", ctxerr.Wrap(ctx, err, "retrieving app config for auth token validation")
|
||||
}
|
||||
|
||||
entraTenantIDs := appConfig.MDM.WindowsEntraTenantIDs.Value
|
||||
if len(entraTenantIDs) == 0 {
|
||||
return "", "", ctxerr.New(ctx, "no entra tenant IDs configured for automatic enrollment")
|
||||
}
|
||||
expectedURL := appConfig.ServerSettings.ServerURL
|
||||
expectedURLParsed, err := url.Parse(expectedURL)
|
||||
if err != nil {
|
||||
return "", "", ctxerr.Wrap(ctx, err, "parsing server URL for auth token validation")
|
||||
}
|
||||
|
||||
// Validate the JWT Auth token by retreving its claims
|
||||
tokenData, err := microsoft_mdm.GetAzureAuthTokenClaims(ctx, authToken.Content)
|
||||
@@ -1027,11 +1040,40 @@ func (svc *Service) authBinarySecurityToken(ctx context.Context, authToken *flee
|
||||
return "", "", fmt.Errorf("binary security token claim failed: %v", err)
|
||||
}
|
||||
|
||||
hasExpectedAudience := false
|
||||
for _, aud := range tokenData.Audience {
|
||||
audURL, err := url.Parse(aud)
|
||||
// The Audience may have multiple values and not everything in the aud will be a URL and that's OK
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if audURL.Host == expectedURLParsed.Host {
|
||||
hasExpectedAudience = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasExpectedAudience {
|
||||
// Log bad audiences here for debugging
|
||||
level.Error(svc.logger).Log(
|
||||
"msg", "unexpected token audience in AzureAD Binary Security Token",
|
||||
"expected_host", expectedURLParsed.Host,
|
||||
"token_audiences", strings.Join(tokenData.Audience, ","),
|
||||
)
|
||||
return "", "", ctxerr.Errorf(ctx, "token audience is not authorized")
|
||||
}
|
||||
if !slices.Contains(entraTenantIDs, tokenData.TenantID) {
|
||||
level.Error(svc.logger).Log(
|
||||
"msg", "unexpected token tenant in AzureAD Binary Security Token",
|
||||
"token_tenant", tokenData.TenantID,
|
||||
)
|
||||
return "", "", ctxerr.New(ctx, "token tenant is not authorized")
|
||||
}
|
||||
|
||||
// No errors, token is authorized
|
||||
return tokenData.UPN, "", nil
|
||||
}
|
||||
|
||||
return "", "", errors.New("token is not authorized")
|
||||
return "", "", ctxerr.New(ctx, "token is not authorized")
|
||||
}
|
||||
|
||||
// ProcessMDMMicrosoftDiscovery handles the Discovery message validation and response
|
||||
|
||||
Reference in New Issue
Block a user