diff --git a/saml/saml.go b/saml/saml.go
index c31e52468248bd2d867620251decb4be50cb8769..64a39e9bcbb5c518185c594f965551328ff3eec8 100644
--- a/saml/saml.go
+++ b/saml/saml.go
@@ -41,7 +41,8 @@ type serviceProvider struct {
 type Config struct {
 	BaseURL string `yaml:"base_url"`
 
-	UsersFile string `yaml:"users_file"`
+	UsersFile      string `yaml:"users_file"`
+	EmailUsernames bool   `yaml:"email_usernames"`
 
 	// SAML X509 credentials.
 	CertificateFile string `yaml:"certificate_file"`
@@ -134,25 +135,63 @@ type userInfo struct {
 	Email string `yaml:"email"`
 }
 
+type userBackend interface {
+	GetUser(string) (*userInfo, bool)
+}
+
 type userFileBackend struct {
-	config *Config
-	users  map[string]userInfo
+	users map[string]*userInfo
+}
+
+func (b *userFileBackend) GetUser(username string) (*userInfo, bool) {
+	info, ok := b.users[username]
+	return info, ok
 }
 
-func newUserFileBackend(config *Config) (*userFileBackend, error) {
-	data, err := ioutil.ReadFile(config.UsersFile)
+func newUserFileBackend(path string) (*userFileBackend, error) {
+	data, err := ioutil.ReadFile(path)
 	if err != nil {
 		return nil, err
 	}
-	var userList []userInfo
+	var userList []*userInfo
 	if err := yaml.Unmarshal(data, &userList); err != nil {
 		return nil, err
 	}
-	users := make(map[string]userInfo)
+	users := make(map[string]*userInfo)
 	for _, u := range userList {
 		users[u.Name] = u
 	}
 	return &userFileBackend{
+		users: users,
+	}, nil
+}
+
+type identityUserBackend struct{}
+
+func (b *identityUserBackend) GetUser(username string) (*userInfo, bool) {
+	return &userInfo{Name: username, Email: username}, true
+}
+
+type sessionProvider struct {
+	config *Config
+	users  userBackend
+}
+
+func newSessionProvider(config *Config) (*sessionProvider, error) {
+	var users userBackend
+	var err error
+	switch {
+	case config.UsersFile != "":
+		users, err = newUserFileBackend(config.UsersFile)
+		if err != nil {
+			return nil, err
+		}
+	case config.EmailUsernames:
+		users = new(identityUserBackend)
+	default:
+		return nil, errors.New("neither users_file or email_usernames are specified")
+	}
+	return &sessionProvider{
 		config: config,
 		users:  users,
 	}, nil
@@ -173,7 +212,7 @@ func matchGroups(user, exp []string) bool {
 	return false
 }
 
-func (b *userFileBackend) GetSession(w http.ResponseWriter, r *http.Request, req *saml.IdpAuthnRequest) *saml.Session {
+func (sp *sessionProvider) GetSession(w http.ResponseWriter, r *http.Request, req *saml.IdpAuthnRequest) *saml.Session {
 	// Check for authentication by verifying the SSO username. We
 	// also need to be able to retrieve user information from the
 	// backend, to match SSO. Group membership, if enabled in our
@@ -185,12 +224,12 @@ func (b *userFileBackend) GetSession(w http.ResponseWriter, r *http.Request, req
 		return nil
 	}
 
-	if !matchGroups(httpsso.Groups(r), b.config.GetSSOGroups(req.ServiceProviderMetadata.ID)) {
+	if !matchGroups(httpsso.Groups(r), sp.config.GetSSOGroups(req.ServiceProviderMetadata.ID)) {
 		http.Error(w, "Forbidden (bad group)", http.StatusForbidden)
 		return nil
 	}
 
-	user, ok := b.users[username]
+	user, ok := sp.users.GetUser(username)
 	if !ok {
 		log.Printf("error: user %s is authenticated but unknown", username)
 		http.Error(w, "User not found", http.StatusInternalServerError)
@@ -252,7 +291,7 @@ func NewSAMLIDP(config *Config) (http.Handler, error) {
 		svc += "/"
 	}
 
-	users, err := newUserFileBackend(config)
+	sp, err := newSessionProvider(config)
 	if err != nil {
 		return nil, err
 	}
@@ -267,7 +306,7 @@ func NewSAMLIDP(config *Config) (http.Handler, error) {
 		MetadataURL:             metadataURL,
 		SSOURL:                  ssoURL,
 		ServiceProviderProvider: config,
-		SessionProvider:         users,
+		SessionProvider:         sp,
 	}
 	h := idp.Handler()