diff --git a/Documentation/connectors/microsoft.md b/Documentation/connectors/microsoft.md
index 11024a6d91e04782fb432597b5386c86f52351b1..4d5dafc8a6f7032dba9c1b82137b9ff0ab17edf2 100644
--- a/Documentation/connectors/microsoft.md
+++ b/Documentation/connectors/microsoft.md
@@ -88,6 +88,9 @@ a member of. `onlySecurityGroups` configuration option restricts the list to
 include only security groups. By default all groups (security, Office 365,
 mailing lists) are included.
 
+By default, dex resolve groups ids to groups names, to keep groups ids, you can
+specify the configuration option `groupNameFormat: id`.
+
 It is possible to require a user to be a member of a particular group in order
 to be successfully authenticated in dex. For example, with the following
 configuration file only the users who are members of at least one of the listed
@@ -110,3 +113,6 @@ connectors:
         - developers
         - devops
 ```
+
+Also, `useGroupsAsWhitelist` configuration option, can restrict the groups
+claims to include only the user's groups that are in the configured `groups`.
\ No newline at end of file
diff --git a/connector/microsoft/microsoft.go b/connector/microsoft/microsoft.go
index b31cfa55c6bd7e392850138142f15bd83f68a20a..d4ce2e675b160d105fe301e0ac85a272986dbd1d 100644
--- a/connector/microsoft/microsoft.go
+++ b/connector/microsoft/microsoft.go
@@ -19,35 +19,50 @@ import (
 	"github.com/dexidp/dex/pkg/log"
 )
 
+// GroupNameFormat represents the format of the group identifier
+// we use type of string instead of int because it's easier to
+// marshall/unmarshall
+type GroupNameFormat string
+
+// Possible values for GroupNameFormat
+const (
+	GroupID   GroupNameFormat = "id"
+	GroupName GroupNameFormat = "name"
+)
+
 const (
 	apiURL = "https://graph.microsoft.com"
 	// Microsoft requires this scope to access user's profile
 	scopeUser = "user.read"
 	// Microsoft requires this scope to list groups the user is a member of
-	// and resolve their UUIDs to groups names.
+	// and resolve their ids to groups names.
 	scopeGroups = "directory.read.all"
 )
 
 // Config holds configuration options for microsoft logins.
 type Config struct {
-	ClientID           string   `json:"clientID"`
-	ClientSecret       string   `json:"clientSecret"`
-	RedirectURI        string   `json:"redirectURI"`
-	Tenant             string   `json:"tenant"`
-	OnlySecurityGroups bool     `json:"onlySecurityGroups"`
-	Groups             []string `json:"groups"`
+	ClientID             string          `json:"clientID"`
+	ClientSecret         string          `json:"clientSecret"`
+	RedirectURI          string          `json:"redirectURI"`
+	Tenant               string          `json:"tenant"`
+	OnlySecurityGroups   bool            `json:"onlySecurityGroups"`
+	Groups               []string        `json:"groups"`
+	GroupNameFormat      GroupNameFormat `json:"groupNameFormat"`
+	UseGroupsAsWhitelist bool            `json:"useGroupsAsWhitelist"`
 }
 
 // Open returns a strategy for logging in through Microsoft.
 func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
 	m := microsoftConnector{
-		redirectURI:        c.RedirectURI,
-		clientID:           c.ClientID,
-		clientSecret:       c.ClientSecret,
-		tenant:             c.Tenant,
-		onlySecurityGroups: c.OnlySecurityGroups,
-		groups:             c.Groups,
-		logger:             logger,
+		redirectURI:          c.RedirectURI,
+		clientID:             c.ClientID,
+		clientSecret:         c.ClientSecret,
+		tenant:               c.Tenant,
+		onlySecurityGroups:   c.OnlySecurityGroups,
+		groups:               c.Groups,
+		groupNameFormat:      c.GroupNameFormat,
+		useGroupsAsWhitelist: c.UseGroupsAsWhitelist,
+		logger:               logger,
 	}
 	// By default allow logins from both personal and business/school
 	// accounts.
@@ -55,6 +70,15 @@ func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error)
 		m.tenant = "common"
 	}
 
+	// By default, use group names
+	switch m.groupNameFormat {
+	case "":
+		m.groupNameFormat = GroupName
+	case GroupID, GroupName:
+	default:
+		return nil, fmt.Errorf("invalid groupNameFormat: %s", m.groupNameFormat)
+	}
+
 	return &m, nil
 }
 
@@ -70,13 +94,15 @@ var (
 )
 
 type microsoftConnector struct {
-	redirectURI        string
-	clientID           string
-	clientSecret       string
-	tenant             string
-	onlySecurityGroups bool
-	groups             []string
-	logger             log.Logger
+	redirectURI          string
+	clientID             string
+	clientSecret         string
+	tenant               string
+	onlySecurityGroups   bool
+	groupNameFormat      GroupNameFormat
+	groups               []string
+	useGroupsAsWhitelist bool
+	logger               log.Logger
 }
 
 func (c *microsoftConnector) isOrgTenant() bool {
@@ -300,24 +326,28 @@ type group struct {
 	Name string `json:"displayName"`
 }
 
-func (c *microsoftConnector) getGroups(ctx context.Context, client *http.Client, userID string) (groups []string, err error) {
-	ids, err := c.getGroupIDs(ctx, client)
+func (c *microsoftConnector) getGroups(ctx context.Context, client *http.Client, userID string) ([]string, error) {
+	userGroups, err := c.getGroupIDs(ctx, client)
 	if err != nil {
-		return groups, err
+		return nil, err
 	}
 
-	groups, err = c.getGroupNames(ctx, client, ids)
-	if err != nil {
-		return
+	if c.groupNameFormat == GroupName {
+		userGroups, err = c.getGroupNames(ctx, client, userGroups)
+		if err != nil {
+			return nil, err
+		}
 	}
 
 	// ensure that the user is in at least one required group
-	filteredGroups := groups_pkg.Filter(groups, c.groups)
+	filteredGroups := groups_pkg.Filter(userGroups, c.groups)
 	if len(c.groups) > 0 && len(filteredGroups) == 0 {
 		return nil, fmt.Errorf("microsoft: user %v not in any of the required groups", userID)
+	} else if c.useGroupsAsWhitelist {
+		return filteredGroups, nil
 	}
 
-	return
+	return userGroups, nil
 }
 
 func (c *microsoftConnector) getGroupIDs(ctx context.Context, client *http.Client) (ids []string, err error) {