Add VerifyPassword to API
It takes in an email and plain text password to verify. If it fails to find a password stored for email, it returns not_found. If it finds the password hash stored but that hash doesn't match the password passed via the API, it returns verified = false, else it returns verified = true. Co-authored-by: Alban Seurat <alban.seurat@me.com>
This commit is contained in:
		
				
					committed by
					
						
						Alban Seurat
					
				
			
			
				
	
			
			
			
						parent
						
							92920c86ea
						
					
				
				
					commit
					dd84e73c0e
				
			@@ -254,6 +254,37 @@ func (d dexAPI) ListPasswords(ctx context.Context, req *api.ListPasswordReq) (*a
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d dexAPI) VerifyPassword(ctx context.Context, req *api.VerifyPasswordReq) (*api.VerifyPasswordResp, error) {
 | 
			
		||||
	if req.Email == "" {
 | 
			
		||||
		return nil, errors.New("no email supplied")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if req.Password == "" {
 | 
			
		||||
		return nil, errors.New("no password to verify supplied")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	password, err := d.s.GetPassword(req.Email)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if err == storage.ErrNotFound {
 | 
			
		||||
			return &api.VerifyPasswordResp{
 | 
			
		||||
				NotFound: true,
 | 
			
		||||
			}, nil
 | 
			
		||||
		}
 | 
			
		||||
		d.logger.Errorf("api: there was an error retrieving the password: %v", err)
 | 
			
		||||
		return nil, fmt.Errorf("verify password: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := bcrypt.CompareHashAndPassword(password.Hash, []byte(req.Password)); err != nil {
 | 
			
		||||
		d.logger.Info("api: password check failed : %v", err)
 | 
			
		||||
		return &api.VerifyPasswordResp{
 | 
			
		||||
			Verified: false,
 | 
			
		||||
		}, nil
 | 
			
		||||
	}
 | 
			
		||||
	return &api.VerifyPasswordResp{
 | 
			
		||||
		Verified: true,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d dexAPI) ListRefresh(ctx context.Context, req *api.ListRefreshReq) (*api.ListRefreshResp, error) {
 | 
			
		||||
	id := new(internal.IDTokenSubject)
 | 
			
		||||
	if err := internal.Unmarshal(req.UserId, id); err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -69,8 +69,9 @@ func TestPassword(t *testing.T) {
 | 
			
		||||
	defer client.Close()
 | 
			
		||||
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	email := "test@example.com"
 | 
			
		||||
	p := api.Password{
 | 
			
		||||
		Email: "test@example.com",
 | 
			
		||||
		Email: email,
 | 
			
		||||
		// bcrypt hash of the value "test1" with cost 10
 | 
			
		||||
		Hash:     []byte("$2a$10$XVMN/Fid.Ks4CXgzo8fpR.iU1khOMsP5g9xQeXuBm1wXjRX8pjUtO"),
 | 
			
		||||
		Username: "test",
 | 
			
		||||
@@ -93,8 +94,56 @@ func TestPassword(t *testing.T) {
 | 
			
		||||
		t.Fatalf("Created password %s twice", createReq.Password.Email)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Attempt to verify valid password and email
 | 
			
		||||
	goodVerifyReq := &api.VerifyPasswordReq{
 | 
			
		||||
		Email:    email,
 | 
			
		||||
		Password: "test1",
 | 
			
		||||
	}
 | 
			
		||||
	goodVerifyResp, err := client.VerifyPassword(ctx, goodVerifyReq)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Unable to run verify password we expected to be valid for correct email: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if !goodVerifyResp.Verified {
 | 
			
		||||
		t.Fatalf("verify password failed for password expected to be valid for correct email. expected %t, found %t", true, goodVerifyResp.Verified)
 | 
			
		||||
	}
 | 
			
		||||
	if goodVerifyResp.NotFound {
 | 
			
		||||
		t.Fatalf("verify password failed to return not found response. expected %t, found %t", false, goodVerifyResp.NotFound)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check not found response for valid password with wrong email
 | 
			
		||||
	badEmailVerifyReq := &api.VerifyPasswordReq{
 | 
			
		||||
		Email:    "somewrongaddress@email.com",
 | 
			
		||||
		Password: "test1",
 | 
			
		||||
	}
 | 
			
		||||
	badEmailVerifyResp, err := client.VerifyPassword(ctx, badEmailVerifyReq)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Unable to run verify password for incorrect email: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if badEmailVerifyResp.Verified {
 | 
			
		||||
		t.Fatalf("verify password passed for password expected to be not found. expected %t, found %t", false, badEmailVerifyResp.Verified)
 | 
			
		||||
	}
 | 
			
		||||
	if !badEmailVerifyResp.NotFound {
 | 
			
		||||
		t.Fatalf("expected not found response for verify password with bad email. expected %t, found %t", true, badEmailVerifyResp.NotFound)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check that wrong password fails
 | 
			
		||||
	badPassVerifyReq := &api.VerifyPasswordReq{
 | 
			
		||||
		Email:    email,
 | 
			
		||||
		Password: "wrong_password",
 | 
			
		||||
	}
 | 
			
		||||
	badPassVerifyResp, err := client.VerifyPassword(ctx, badPassVerifyReq)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Unable to run verify password for password we expected to be invalid: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if badPassVerifyResp.Verified {
 | 
			
		||||
		t.Fatalf("verify password passed for password we expected to fail. expected %t, found %t", false, badPassVerifyResp.Verified)
 | 
			
		||||
	}
 | 
			
		||||
	if badPassVerifyResp.NotFound {
 | 
			
		||||
		t.Fatalf("did not expect expected not found response for verify password with bad email. expected %t, found %t", false, badPassVerifyResp.NotFound)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	updateReq := api.UpdatePasswordReq{
 | 
			
		||||
		Email:       "test@example.com",
 | 
			
		||||
		Email:       email,
 | 
			
		||||
		NewUsername: "test1",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user