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:
Tyler Cloke
2018-08-06 12:04:56 -07:00
committed by Alban Seurat
parent 92920c86ea
commit dd84e73c0e
6 changed files with 274 additions and 57 deletions

View File

@@ -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 {

View File

@@ -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",
}