Example #1
0
func TestServerTokenUnrecognizedKey(t *testing.T) {
	ci := client.Client{
		Credentials: oidc.ClientCredentials{
			ID:     testClientID,
			Secret: clientTestSecret,
		},
		Metadata: oidc.ClientMetadata{
			RedirectURIs: []url.URL{
				validRedirURL,
			},
		},
	}

	clients := []client.Client{ci}
	dbm := db.NewMemDB()
	clientIDGenerator := func(hostport string) (string, error) {
		return hostport, nil
	}
	secGen := func() ([]byte, error) {
		return []byte("secret"), nil
	}
	clientRepo := db.NewClientRepo(dbm)
	clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbm), clients, clientmanager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
	if err != nil {
		t.Fatalf("Failed to create client identity manager: %v", err)
	}
	km := &StaticKeyManager{
		signer: &StaticSigner{sig: []byte("beer"), err: nil},
	}
	sm := manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB()))

	srv := &Server{
		IssuerURL:      url.URL{Scheme: "http", Host: "server.example.com"},
		KeyManager:     km,
		SessionManager: sm,
		ClientRepo:     clientRepo,
		ClientManager:  clientManager,
	}

	sessionID, err := sm.NewSession("connector_id", ci.Credentials.ID, "bogus", url.URL{}, "", false, []string{"openid", "offline_access"})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	_, err = sm.AttachRemoteIdentity(sessionID, oidc.Identity{})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	jwt, token, err := srv.CodeToken(ci.Credentials, "foo")
	if err == nil {
		t.Fatalf("Expected non-nil error")
	}
	if jwt != nil {
		t.Fatalf("Expected nil jwt")
	}
	if token != "" {
		t.Fatalf("Expected empty refresh token")
	}
}
Example #2
0
func connect(t *testing.T) *gorp.DbMap {
	dsn := os.Getenv("DEX_TEST_DSN")
	if dsn == "" {
		return db.NewMemDB()

	}
	c, err := db.NewConnection(db.Config{DSN: dsn})
	if err != nil {
		t.Fatalf("Unable to connect to database: %v", err)
	}
	if err = c.DropTablesIfExists(); err != nil {
		t.Fatalf("Unable to drop database tables: %v", err)
	}

	if err = db.DropMigrationsTable(c); err != nil {
		t.Fatalf("Unable to drop migration table: %v", err)
	}

	n, err := db.MigrateToLatest(c)
	if err != nil {
		t.Fatalf("Unable to migrate: %v", err)
	}
	if n == 0 {
		t.Fatalf("No migrations performed")
	}

	return c
}
Example #3
0
func makeTestFixtures() *testFixtures {
	f := &testFixtures{}

	dbMap := db.NewMemDB()
	clients := []client.Client{
		{
			Credentials: oidc.ClientCredentials{
				ID:     "client.example.com",
				Secret: goodSecret,
			},
			Metadata: oidc.ClientMetadata{
				RedirectURIs: []url.URL{
					{Scheme: "http", Host: "client.example.com", Path: "/"},
				},
			},
			Admin: true,
		},
	}
	clientIDGenerator := func(hostport string) (string, error) {
		return hostport, nil
	}
	secGen := func() ([]byte, error) {
		return []byte("secret"), nil
	}
	f.clientRepo = db.NewClientRepo(dbMap)
	clientManager, err := NewClientManagerFromClients(f.clientRepo, db.TransactionFactory(dbMap), clients, ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
	if err != nil {
		panic("Failed to create client manager: " + err.Error())
	}
	f.mgr = clientManager
	return f
}
Example #4
0
func mockServer(cis []client.LoadableClient) (*server.Server, error) {
	dbMap := db.NewMemDB()
	k, err := key.GeneratePrivateKey()
	if err != nil {
		return nil, fmt.Errorf("Unable to generate private key: %v", err)
	}

	km := key.NewPrivateKeyManager()
	err = km.Set(key.NewPrivateKeySet([]*key.PrivateKey{k}, time.Now().Add(time.Minute)))
	if err != nil {
		return nil, err
	}

	clientRepo, clientManager, err := makeClientRepoAndManager(dbMap, cis)
	if err != nil {
		return nil, err
	}

	sm := manager.NewSessionManager(db.NewSessionRepo(dbMap), db.NewSessionKeyRepo(dbMap))
	srv := &server.Server{
		IssuerURL:      url.URL{Scheme: "http", Host: "server.example.com"},
		KeyManager:     km,
		ClientRepo:     clientRepo,
		ClientManager:  clientManager,
		SessionManager: sm,
	}

	return srv, nil
}
Example #5
0
// TestClientSample makes sure that the clients.json.sample file is valid and can be loaded properly.
func TestClientSample(t *testing.T) {
	f, err := os.Open(clientsFile)
	if err != nil {
		t.Fatalf("could not open file %q: %v", clientsFile, err)
	}
	defer f.Close()

	clients, err := client.ClientsFromReader(f)
	if err != nil {
		t.Fatalf("Error loading Clients: %v", err)
	}

	memDB := db.NewMemDB()
	repo, err := db.NewClientRepoFromClients(memDB, clients)
	if err != nil {
		t.Fatalf("Error creating Clients: %v", err)
	}

	mgr := manager.NewClientManager(repo, db.TransactionFactory(memDB), manager.ManagerOptions{})

	for i, c := range clients {
		ok, err := mgr.Authenticate(c.Client.Credentials)
		if !ok {
			t.Errorf("case %d: couldn't authenticate", i)
		}
		if err != nil {
			t.Errorf("case %d: error authenticating: %v", i, err)
		}
	}

}
Example #6
0
func makeUserObjects(users []user.UserWithRemoteIdentities, passwords []user.PasswordInfo) (*gorp.DbMap, user.UserRepo, user.PasswordInfoRepo, *manager.UserManager) {
	dbMap := db.NewMemDB()
	ur := func() user.UserRepo {
		repo, err := db.NewUserRepoFromUsers(dbMap, users)
		if err != nil {
			panic("Failed to create user repo: " + err.Error())
		}
		return repo
	}()
	pwr := func() user.PasswordInfoRepo {
		repo, err := db.NewPasswordInfoRepoFromPasswordInfos(dbMap, passwords)
		if err != nil {
			panic("Failed to create password info repo: " + err.Error())
		}
		return repo
	}()

	ccr := func() connector.ConnectorConfigRepo {
		repo := db.NewConnectorConfigRepo(dbMap)
		c := []connector.ConnectorConfig{&connector.LocalConnectorConfig{ID: "local"}}
		if err := repo.Set(c); err != nil {
			panic(err)
		}
		return repo
	}()

	um := manager.NewUserManager(ur, pwr, ccr, db.TransactionFactory(dbMap), manager.ManagerOptions{})
	um.Clock = clock
	return dbMap, ur, pwr, um
}
Example #7
0
func TestServerLoginUnrecognizedSessionKey(t *testing.T) {
	clients := []client.Client{
		client.Client{
			Credentials: oidc.ClientCredentials{
				ID: testClientID, Secret: clientTestSecret,
			},
			Metadata: oidc.ClientMetadata{
				RedirectURIs: []url.URL{
					validRedirURL,
				},
			},
		},
	}
	dbm := db.NewMemDB()
	clientIDGenerator := func(hostport string) (string, error) {
		return hostport, nil
	}
	secGen := func() ([]byte, error) {
		return []byte("secret"), nil
	}
	clientRepo := db.NewClientRepo(dbm)
	clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbm), clients, clientmanager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
	if err != nil {
		t.Fatalf("Failed to create client identity manager: %v", err)
	}
	km := &StaticKeyManager{
		signer: &StaticSigner{sig: nil, err: errors.New("fail")},
	}
	sm := manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB()))
	srv := &Server{
		IssuerURL:      url.URL{Scheme: "http", Host: "server.example.com"},
		KeyManager:     km,
		SessionManager: sm,
		ClientRepo:     clientRepo,
		ClientManager:  clientManager,
	}

	ident := oidc.Identity{ID: "YYY", Name: "elroy", Email: "*****@*****.**"}
	code, err := srv.Login(ident, testClientID)
	if err == nil {
		t.Fatalf("Expected non-nil error")
	}

	if code != "" {
		t.Fatalf("Expected empty code, got=%s", code)
	}
}
Example #8
0
func TestServerTokenUnrecognizedKey(t *testing.T) {
	ci := oidc.ClientIdentity{
		Credentials: oidc.ClientCredentials{
			ID:     "XXX",
			Secret: clientTestSecret,
		},
	}
	ciRepo := func() client.ClientIdentityRepo {
		repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ci})
		if err != nil {
			t.Fatalf("Failed to create client identity repo: %v", err)
		}
		return repo
	}()
	km := &StaticKeyManager{
		signer: &StaticSigner{sig: []byte("beer"), err: nil},
	}
	sm := manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB()))

	srv := &Server{
		IssuerURL:          url.URL{Scheme: "http", Host: "server.example.com"},
		KeyManager:         km,
		SessionManager:     sm,
		ClientIdentityRepo: ciRepo,
	}

	sessionID, err := sm.NewSession("connector_id", ci.Credentials.ID, "bogus", url.URL{}, "", false, []string{"openid", "offline_access"})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	_, err = sm.AttachRemoteIdentity(sessionID, oidc.Identity{})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	jwt, token, err := srv.CodeToken(ci.Credentials, "foo")
	if err == nil {
		t.Fatalf("Expected non-nil error")
	}
	if jwt != nil {
		t.Fatalf("Expected nil jwt")
	}
	if token != "" {
		t.Fatalf("Expected empty refresh token")
	}
}
Example #9
0
func newSessionKeyRepo(t *testing.T) (session.SessionKeyRepo, clockwork.FakeClock) {
	clock := clockwork.NewFakeClock()
	if os.Getenv("DEX_TEST_DSN") == "" {
		return db.NewSessionKeyRepoWithClock(db.NewMemDB(), clock), clock
	}
	dbMap := connect(t)
	return db.NewSessionKeyRepoWithClock(dbMap, clock), clock
}
Example #10
0
File: repo.go Project: Tecsisa/dex
// NewTestRefreshTokenRepo returns a test repo whose tokens monotonically increase.
// The tokens are in the form { refresh-1, refresh-2 ... refresh-n}.
func NewTestRefreshTokenRepo() refresh.RefreshTokenRepo {
	var tokenIdx int
	tokenGenerator := func() ([]byte, error) {
		tokenIdx++
		return []byte(fmt.Sprintf("refresh-%d", tokenIdx)), nil
	}
	return db.NewRefreshTokenRepoWithGenerator(db.NewMemDB(), tokenGenerator)
}
Example #11
0
func makeTestFixtures() *testFixtures {
	f := &testFixtures{}

	dbMap := db.NewMemDB()
	f.ur = func() user.UserRepo {
		repo, err := db.NewUserRepoFromUsers(dbMap, []user.UserWithRemoteIdentities{
			{
				User: user.User{
					ID:          "ID-1",
					Email:       "*****@*****.**",
					DisplayName: "Name-1",
				},
			},
			{
				User: user.User{
					ID:          "ID-2",
					Email:       "*****@*****.**",
					DisplayName: "Name-2",
				},
			},
		})
		if err != nil {
			panic("Failed to create user repo: " + err.Error())
		}
		return repo
	}()

	f.pwr = func() user.PasswordInfoRepo {
		repo, err := db.NewPasswordInfoRepoFromPasswordInfos(dbMap, []user.PasswordInfo{
			{
				UserID:   "ID-1",
				Password: []byte("hi."),
			},
		})
		if err != nil {
			panic("Failed to create user repo: " + err.Error())
		}
		return repo
	}()

	f.ccr = func() connector.ConnectorConfigRepo {
		c := []connector.ConnectorConfig{&connector.LocalConnectorConfig{ID: "local"}}
		repo := db.NewConnectorConfigRepo(dbMap)
		if err := repo.Set(c); err != nil {
			panic(err)
		}
		return repo
	}()

	f.mgr = manager.NewUserManager(f.ur, f.pwr, f.ccr, db.TransactionFactory(dbMap), manager.ManagerOptions{})
	f.cm = clientmanager.NewClientManager(f.cr, db.TransactionFactory(dbMap), clientmanager.ManagerOptions{})
	f.adAPI = NewAdminAPI(f.ur, f.pwr, f.cr, f.ccr, f.mgr, f.cm, "local")

	return f
}
Example #12
0
func TestServerNewSession(t *testing.T) {
	sm := manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB()))
	srv := &Server{
		SessionManager: sm,
	}

	state := "pants"
	nonce := "oncenay"
	ci := client.Client{
		Credentials: oidc.ClientCredentials{
			ID:     testClientID,
			Secret: clientTestSecret,
		},
		Metadata: oidc.ClientMetadata{
			RedirectURIs: []url.URL{
				url.URL{
					Scheme: "http",
					Host:   "client.example.com",
					Path:   "/callback",
				},
			},
		},
	}

	key, err := srv.NewSession("bogus_idpc", ci.Credentials.ID, state, ci.Metadata.RedirectURIs[0], nonce, false, []string{"openid"})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	sessionID, err := sm.ExchangeKey(key)
	if err != nil {
		t.Fatalf("Session not retreivable: %v", err)
	}

	ses, err := sm.AttachRemoteIdentity(sessionID, oidc.Identity{})
	if err != nil {
		t.Fatalf("Unable to add Identity to Session: %v", err)
	}

	if !reflect.DeepEqual(ci.Metadata.RedirectURIs[0], ses.RedirectURL) {
		t.Fatalf("Session created with incorrect RedirectURL: want=%#v got=%#v", ci.Metadata.RedirectURIs[0], ses.RedirectURL)
	}

	if ci.Credentials.ID != ses.ClientID {
		t.Fatalf("Session created with incorrect ClientID: want=%q got=%q", ci.Credentials.ID, ses.ClientID)
	}

	if state != ses.ClientState {
		t.Fatalf("Session created with incorrect State: want=%q got=%q", state, ses.ClientState)
	}

	if nonce != ses.Nonce {
		t.Fatalf("Session created with incorrect Nonce: want=%q got=%q", nonce, ses.Nonce)
	}
}
Example #13
0
func TestCreate(t *testing.T) {
	dbm := db.NewMemDB()
	repo := db.NewClientRepo(dbm)
	manager := manager.NewClientManager(repo, db.TransactionFactory(dbm), manager.ManagerOptions{})
	res := &clientResource{manager: manager}
	tests := [][]string{
		[]string{"http://example.com"},
		[]string{"https://example.com"},
		[]string{"http://example.com/foo"},
		[]string{"http://example.com/bar", "http://example.com/foo"},
	}
	endpoint := "http://example.com/clients"

	for i, tt := range tests {
		body := strings.NewReader(fmt.Sprintf(`{"redirectURIs":["%s"]}`, strings.Join(tt, `","`)))
		r, err := http.NewRequest("POST", endpoint, body)
		if err != nil {
			t.Fatalf("Failed creating http.Request: %v", err)
		}
		r.Header.Set("content-type", "application/json")
		w := httptest.NewRecorder()
		res.ServeHTTP(w, r)

		if w.Code != http.StatusCreated {
			t.Errorf("case %d: invalid response code, want=%d, got=%d", i, http.StatusCreated, w.Code)
		}

		var client schema.ClientWithSecret
		if err := json.Unmarshal(w.Body.Bytes(), &client); err != nil {
			t.Errorf("case %d: unexpected error=%v", i, err)
		}
		if len(client.RedirectURIs) != len(tt) {
			t.Errorf("case %d: unexpected number of redirect URIs, want=%d, got=%d", i, len(tt), len(client.RedirectURIs))
		}

		if !reflect.DeepEqual(tt, client.RedirectURIs) {
			t.Errorf("case %d: unexpected client redirect URIs: want=%v got=%v", i, tt, client.RedirectURIs)
		}

		if client.Id == "" {
			t.Errorf("case %d: empty client ID in response", i)
		}

		if client.Secret == "" {
			t.Errorf("case %d: empty client secret in response", i)
		}

		wantLoc := fmt.Sprintf("%s/%s", endpoint, client.Id)
		gotLoc := w.Header().Get("Location")
		if gotLoc != wantLoc {
			t.Errorf("case %d: invalid location header, want=%v, got=%v", i, wantLoc, gotLoc)
		}
	}
}
Example #14
0
func newConnectorConfigRepo(t *testing.T, configs []connector.ConnectorConfig) connector.ConnectorConfigRepo {
	var dbMap *gorp.DbMap
	if os.Getenv("DEX_TEST_DSN") == "" {
		dbMap = db.NewMemDB()
	} else {
		dbMap = connect(t)
	}
	repo := db.NewConnectorConfigRepo(dbMap)
	if err := repo.Set(configs); err != nil {
		t.Fatalf("Unable to set connector configs: %v", err)
	}
	return repo
}
Example #15
0
func newPasswordInfoRepo(t *testing.T) user.PasswordInfoRepo {
	var dbMap *gorp.DbMap
	if os.Getenv("DEX_TEST_DSN") == "" {
		dbMap = db.NewMemDB()
	} else {
		dbMap = connect(t)
	}
	repo, err := db.NewPasswordInfoRepoFromPasswordInfos(dbMap, testPWs)
	if err != nil {
		t.Fatalf("Unable to add password infos: %v", err)
	}
	return repo
}
Example #16
0
func newClientIdentityRepo(t *testing.T) client.ClientIdentityRepo {
	dsn := os.Getenv("DEX_TEST_DSN")
	var dbMap *gorp.DbMap
	if dsn == "" {
		dbMap = db.NewMemDB()
	} else {
		dbMap = connect(t)
	}
	repo, err := db.NewClientIdentityRepoFromClients(dbMap, testClients)
	if err != nil {
		t.Fatalf("failed to create client repo from clients: %v", err)
	}
	return repo
}
Example #17
0
func newRefreshRepo(t *testing.T, users []user.UserWithRemoteIdentities, clients []client.Client) refresh.RefreshTokenRepo {
	var dbMap *gorp.DbMap
	if dsn := os.Getenv("DEX_TEST_DSN"); dsn == "" {
		dbMap = db.NewMemDB()
	} else {
		dbMap = connect(t)
	}
	if _, err := db.NewUserRepoFromUsers(dbMap, users); err != nil {
		t.Fatalf("Unable to add users: %v", err)
	}
	if _, err := manager.NewClientManagerFromClients(db.NewClientRepo(dbMap), db.TransactionFactory(dbMap), clients, manager.ManagerOptions{}); err != nil {
		t.Fatalf("Unable to add clients: %v", err)
	}
	return db.NewRefreshTokenRepo(dbMap)
}
Example #18
0
func TestServerLoginUnrecognizedSessionKey(t *testing.T) {
	ciRepo := func() client.ClientIdentityRepo {
		repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{
			oidc.ClientIdentity{
				Credentials: oidc.ClientCredentials{
					ID: "XXX", Secret: clientTestSecret,
				},
			},
		})
		if err != nil {
			t.Fatalf("Failed to create client identity repo: %v", err)
		}
		return repo
	}()

	km := &StaticKeyManager{
		signer: &StaticSigner{sig: nil, err: errors.New("fail")},
	}
	sm := manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB()))
	srv := &Server{
		IssuerURL:          url.URL{Scheme: "http", Host: "server.example.com"},
		KeyManager:         km,
		SessionManager:     sm,
		ClientIdentityRepo: ciRepo,
	}

	ident := oidc.Identity{ID: "YYY", Name: "elroy", Email: "*****@*****.**"}
	code, err := srv.Login(ident, "XXX")
	if err == nil {
		t.Fatalf("Expected non-nil error")
	}

	if code != "" {
		t.Fatalf("Expected empty code, got=%s", code)
	}
}
Example #19
0
func newUserRepo(t *testing.T, users []user.UserWithRemoteIdentities) user.UserRepo {
	if users == nil {
		users = []user.UserWithRemoteIdentities{}
	}
	var dbMap *gorp.DbMap
	if os.Getenv("DEX_TEST_DSN") == "" {
		dbMap = db.NewMemDB()
	} else {
		dbMap = connect(t)
	}
	repo, err := db.NewUserRepoFromUsers(dbMap, users)
	if err != nil {
		t.Fatalf("Unable to add users: %v", err)
	}
	return repo
}
Example #20
0
func makeNewUserRepo() (user.UserRepo, error) {
	userRepo := db.NewUserRepo(db.NewMemDB())

	id := "testid-1"
	err := userRepo.Create(nil, user.User{
		ID:    id,
		Email: "*****@*****.**",
	})
	if err != nil {
		return nil, err
	}

	err = userRepo.AddRemoteIdentity(nil, id, user.RemoteIdentity{
		ConnectorID: "test_connector_id",
		ID:          "YYY",
	})
	if err != nil {
		return nil, err
	}

	return userRepo, nil
}
Example #21
0
func mockServer(cis []client.Client) (*server.Server, error) {
	dbMap := db.NewMemDB()
	k, err := key.GeneratePrivateKey()
	if err != nil {
		return nil, fmt.Errorf("Unable to generate private key: %v", err)
	}

	km := key.NewPrivateKeyManager()
	err = km.Set(key.NewPrivateKeySet([]*key.PrivateKey{k}, time.Now().Add(time.Minute)))
	if err != nil {
		return nil, err
	}

	clientIDGenerator := func(hostport string) (string, error) {
		return hostport, nil
	}
	secGen := func() ([]byte, error) {
		return []byte("secret"), nil
	}
	clientRepo := db.NewClientRepo(dbMap)
	clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbMap), cis, clientmanager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
	if err != nil {
		return nil, err
	}

	sm := manager.NewSessionManager(db.NewSessionRepo(dbMap), db.NewSessionKeyRepo(dbMap))
	srv := &server.Server{
		IssuerURL:      url.URL{Scheme: "http", Host: "server.example.com"},
		KeyManager:     km,
		ClientRepo:     clientRepo,
		ClientManager:  clientManager,
		SessionManager: sm,
	}

	return srv, nil
}
Example #22
0
func TestServerLoginDisabledUser(t *testing.T) {
	ci := client.Client{
		Credentials: oidc.ClientCredentials{
			ID:     testClientID,
			Secret: clientTestSecret,
		},
		Metadata: oidc.ClientMetadata{
			RedirectURIs: []url.URL{
				validRedirURL,
			},
		},
	}
	clients := []client.Client{ci}
	dbm := db.NewMemDB()
	clientIDGenerator := func(hostport string) (string, error) {
		return hostport, nil
	}
	secGen := func() ([]byte, error) {
		return []byte("secret"), nil
	}
	clientRepo := db.NewClientRepo(dbm)
	clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbm), clients, clientmanager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
	if err != nil {
		t.Fatalf("Failed to create client identity manager: %v", err)
	}
	km := &StaticKeyManager{
		signer: &StaticSigner{sig: []byte("beer"), err: nil},
	}

	sm := manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB()))
	sm.GenerateCode = staticGenerateCodeFunc("fakecode")
	sessionID, err := sm.NewSession("test_connector_id", ci.Credentials.ID, "bogus", ci.Metadata.RedirectURIs[0], "", false, []string{"openid"})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	userRepo, err := makeNewUserRepo()
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	err = userRepo.Create(nil, user.User{
		ID:       "disabled-1",
		Email:    "*****@*****.**",
		Disabled: true,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	err = userRepo.AddRemoteIdentity(nil, "disabled-1", user.RemoteIdentity{
		ConnectorID: "test_connector_id",
		ID:          "disabled-connector-id",
	})

	srv := &Server{
		IssuerURL:      url.URL{Scheme: "http", Host: "server.example.com"},
		KeyManager:     km,
		SessionManager: sm,
		ClientRepo:     clientRepo,
		ClientManager:  clientManager,
		UserRepo:       userRepo,
	}

	ident := oidc.Identity{ID: "disabled-connector-id", Name: "elroy", Email: "*****@*****.**"}
	key, err := sm.NewSessionKey(sessionID)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	_, err = srv.Login(ident, key)
	if err == nil {
		t.Errorf("disabled user was allowed to log in")
	}
}
Example #23
0
func TestClientToken(t *testing.T) {
	now := time.Now()
	tomorrow := now.Add(24 * time.Hour)
	validClientID := "valid-client"
	ci := oidc.ClientIdentity{
		Credentials: oidc.ClientCredentials{
			ID:     validClientID,
			Secret: base64.URLEncoding.EncodeToString([]byte("secret")),
		},
		Metadata: oidc.ClientMetadata{
			RedirectURIs: []url.URL{
				{Scheme: "https", Host: "authn.example.com", Path: "/callback"},
			},
		},
	}
	repo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{ci})
	if err != nil {
		t.Fatalf("Failed to create client identity repo: %v", err)
	}

	privKey, err := key.GeneratePrivateKey()
	if err != nil {
		t.Fatalf("Failed to generate private key, error=%v", err)
	}
	signer := privKey.Signer()
	pubKey := *key.NewPublicKey(privKey.JWK())

	validIss := "https://example.com"

	makeToken := func(iss, sub, aud string, iat, exp time.Time) string {
		claims := oidc.NewClaims(iss, sub, aud, iat, exp)
		jwt, err := jose.NewSignedJWT(claims, signer)
		if err != nil {
			t.Fatalf("Failed to generate JWT, error=%v", err)
		}
		return jwt.Encode()
	}

	validJWT := makeToken(validIss, validClientID, validClientID, now, tomorrow)
	invalidJWT := makeToken("", "", "", now, tomorrow)

	tests := []struct {
		keys     []key.PublicKey
		repo     client.ClientIdentityRepo
		header   string
		wantCode int
	}{
		// valid token
		{
			keys:     []key.PublicKey{pubKey},
			repo:     repo,
			header:   fmt.Sprintf("BEARER %s", validJWT),
			wantCode: http.StatusOK,
		},
		// invalid token
		{
			keys:     []key.PublicKey{pubKey},
			repo:     repo,
			header:   fmt.Sprintf("BEARER %s", invalidJWT),
			wantCode: http.StatusUnauthorized,
		},
		// empty header
		{
			keys:     []key.PublicKey{pubKey},
			repo:     repo,
			header:   "",
			wantCode: http.StatusUnauthorized,
		},
		// unparsable token
		{
			keys:     []key.PublicKey{pubKey},
			repo:     repo,
			header:   "BEARER xxx",
			wantCode: http.StatusUnauthorized,
		},
		// no verification keys
		{
			keys:     []key.PublicKey{},
			repo:     repo,
			header:   fmt.Sprintf("BEARER %s", validJWT),
			wantCode: http.StatusUnauthorized,
		},
		// nil repo
		{
			keys:     []key.PublicKey{pubKey},
			repo:     nil,
			header:   fmt.Sprintf("BEARER %s", validJWT),
			wantCode: http.StatusUnauthorized,
		},
		// empty repo
		{
			keys:     []key.PublicKey{pubKey},
			repo:     db.NewClientIdentityRepo(db.NewMemDB()),
			header:   fmt.Sprintf("BEARER %s", validJWT),
			wantCode: http.StatusUnauthorized,
		},
		// client not in repo
		{
			keys:     []key.PublicKey{pubKey},
			repo:     repo,
			header:   fmt.Sprintf("BEARER %s", makeToken(validIss, "DOESNT-EXIST", "DOESNT-EXIST", now, tomorrow)),
			wantCode: http.StatusUnauthorized,
		},
	}

	for i, tt := range tests {
		w := httptest.NewRecorder()
		mw := &clientTokenMiddleware{
			issuerURL: validIss,
			ciRepo:    tt.repo,
			keysFunc: func() ([]key.PublicKey, error) {
				return tt.keys, nil
			},
			next: staticHandler{},
		}
		req := &http.Request{
			Header: http.Header{
				"Authorization": []string{tt.header},
			},
		}

		mw.ServeHTTP(w, req)
		if tt.wantCode != w.Code {
			t.Errorf("case %d: invalid response code, want=%d, got=%d", i, tt.wantCode, w.Code)
		}
	}
}
Example #24
0
func TestServerLogin(t *testing.T) {
	ci := client.Client{
		Credentials: oidc.ClientCredentials{
			ID:     testClientID,
			Secret: clientTestSecret,
		},
		Metadata: oidc.ClientMetadata{
			RedirectURIs: []url.URL{
				url.URL{
					Scheme: "http",
					Host:   "client.example.com",
					Path:   "/callback",
				},
			},
		},
	}

	dbm := db.NewMemDB()
	clientRepo := db.NewClientRepo(dbm)
	clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbm), []client.Client{ci}, clientmanager.ManagerOptions{})
	if err != nil {
		t.Fatalf("Failed to create client identity manager: %v", err)
	}

	km := &StaticKeyManager{
		signer: &StaticSigner{sig: []byte("beer"), err: nil},
	}

	sm := manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB()))
	sm.GenerateCode = staticGenerateCodeFunc("fakecode")
	sessionID, err := sm.NewSession("test_connector_id", ci.Credentials.ID, "bogus", ci.Metadata.RedirectURIs[0], "", false, []string{"openid"})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	userRepo, err := makeNewUserRepo()
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	srv := &Server{
		IssuerURL:      url.URL{Scheme: "http", Host: "server.example.com"},
		KeyManager:     km,
		SessionManager: sm,
		ClientRepo:     clientRepo,
		ClientManager:  clientManager,
		UserRepo:       userRepo,
	}

	ident := oidc.Identity{ID: "YYY", Name: "elroy", Email: "*****@*****.**"}
	key, err := sm.NewSessionKey(sessionID)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	redirectURL, err := srv.Login(ident, key)
	if err != nil {
		t.Fatalf("Unexpected err from Server.Login: %v", err)
	}

	wantRedirectURL := "http://client.example.com/callback?code=fakecode&state=bogus"
	if wantRedirectURL != redirectURL {
		t.Fatalf("Unexpected redirectURL: want=%q, got=%q", wantRedirectURL, redirectURL)
	}
}
Example #25
0
func TestHTTPExchangeTokenRefreshToken(t *testing.T) {
	password, err := user.NewPasswordFromPlaintext("woof")
	if err != nil {
		t.Fatalf("unexpectd error: %q", err)
	}

	passwordInfo := user.PasswordInfo{
		UserID:   "elroy77",
		Password: password,
	}

	cfg := &connector.LocalConnectorConfig{
		ID: "local",
	}

	validRedirURL := url.URL{
		Scheme: "http",
		Host:   "client.example.com",
		Path:   "/callback",
	}
	ci := client.Client{
		Credentials: oidc.ClientCredentials{
			ID:     validRedirURL.Host,
			Secret: base64.URLEncoding.EncodeToString([]byte("secret")),
		},
		Metadata: oidc.ClientMetadata{
			RedirectURIs: []url.URL{
				validRedirURL,
			},
		},
	}

	dbMap := db.NewMemDB()
	clientRepo, clientManager, err := makeClientRepoAndManager(dbMap,
		[]client.LoadableClient{{
			Client: ci,
		}})
	if err != nil {
		t.Fatalf("Failed to create client identity manager: " + err.Error())
	}

	passwordInfoRepo, err := db.NewPasswordInfoRepoFromPasswordInfos(db.NewMemDB(), []user.PasswordInfo{passwordInfo})
	if err != nil {
		t.Fatalf("Failed to create password info repo: %v", err)
	}

	issuerURL := url.URL{Scheme: "http", Host: "server.example.com"}
	sm := manager.NewSessionManager(db.NewSessionRepo(dbMap), db.NewSessionKeyRepo(dbMap))

	k, err := key.GeneratePrivateKey()
	if err != nil {
		t.Fatalf("Unable to generate RSA key: %v", err)
	}

	km := key.NewPrivateKeyManager()
	err = km.Set(key.NewPrivateKeySet([]*key.PrivateKey{k}, time.Now().Add(time.Minute)))
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	usr := user.User{
		ID:          "ID-test",
		Email:       "*****@*****.**",
		DisplayName: "displayname",
	}
	userRepo := db.NewUserRepo(db.NewMemDB())
	if err := userRepo.Create(nil, usr); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo()

	srv := &server.Server{
		IssuerURL:        issuerURL,
		KeyManager:       km,
		SessionManager:   sm,
		ClientRepo:       clientRepo,
		ClientManager:    clientManager,
		Templates:        template.New(connector.LoginPageTemplateName),
		Connectors:       []connector.Connector{},
		UserRepo:         userRepo,
		PasswordInfoRepo: passwordInfoRepo,
		RefreshTokenRepo: refreshTokenRepo,
	}

	if err = srv.AddConnector(cfg); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	sClient := &phttp.HandlerClient{Handler: srv.HTTPHandler()}
	pcfg, err := oidc.FetchProviderConfig(sClient, issuerURL.String())
	if err != nil {
		t.Fatalf("Failed to fetch provider config: %v", err)
	}

	ks := key.NewPublicKeySet([]jose.JWK{k.JWK()}, time.Now().Add(1*time.Hour))

	ccfg := oidc.ClientConfig{
		HTTPClient:     sClient,
		ProviderConfig: pcfg,
		Credentials:    ci.Credentials,
		RedirectURL:    validRedirURL.String(),
		KeySet:         *ks,
	}

	cl, err := oidc.NewClient(ccfg)
	if err != nil {
		t.Fatalf("Failed creating oidc.Client: %v", err)
	}

	m := http.NewServeMux()

	var claims jose.Claims
	var refresh string

	m.HandleFunc("/callback", handleCallbackFunc(cl, &claims, &refresh))
	cClient := &phttp.HandlerClient{Handler: m}

	// this will actually happen due to some interaction between the
	// end-user and a remote identity provider
	sessionID, err := sm.NewSession("bogus_idpc", ci.Credentials.ID, "bogus", url.URL{}, "", false, []string{"openid", "offline_access", "email", "profile"})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if _, err = sm.AttachRemoteIdentity(sessionID, passwordInfo.Identity()); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	if _, err = sm.AttachUser(sessionID, usr.ID); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	key, err := sm.NewSessionKey(sessionID)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	req, err := http.NewRequest("GET", fmt.Sprintf("http://client.example.com/callback?code=%s", key), nil)
	if err != nil {
		t.Fatalf("Failed creating HTTP request: %v", err)
	}

	resp, err := cClient.Do(req)
	if err != nil {
		t.Fatalf("Failed resolving HTTP requests against /callback: %v", err)
	}

	if err := verifyUserClaims(claims, &ci, &usr, issuerURL); err != nil {
		t.Fatalf("Failed to verify claims: %v", err)
	}

	if resp.StatusCode != http.StatusOK {
		t.Fatalf("Received status code %d, want %d", resp.StatusCode, http.StatusOK)
	}

	if refresh == "" {
		t.Fatalf("No refresh token")
	}

	// Use refresh token to get a new ID token.
	token, err := cl.RefreshToken(refresh)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	claims, err = token.Claims()
	if err != nil {
		t.Fatalf("Failed parsing claims from client token: %v", err)
	}

	if err := verifyUserClaims(claims, &ci, &usr, issuerURL); err != nil {
		t.Fatalf("Failed to verify claims: %v", err)
	}
}
Example #26
0
func TestServerCodeToken(t *testing.T) {
	ci := client.Client{
		Credentials: oidc.ClientCredentials{
			ID:     testClientID,
			Secret: clientTestSecret,
		},
		Metadata: oidc.ClientMetadata{
			RedirectURIs: []url.URL{
				validRedirURL,
			},
		},
	}
	clients := []client.Client{ci}
	dbm := db.NewMemDB()
	clientIDGenerator := func(hostport string) (string, error) {
		return hostport, nil
	}
	secGen := func() ([]byte, error) {
		return []byte("secret"), nil
	}
	clientRepo := db.NewClientRepo(dbm)
	clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbm), clients, clientmanager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
	if err != nil {
		t.Fatalf("Failed to create client identity manager: %v", err)
	}
	km := &StaticKeyManager{
		signer: &StaticSigner{sig: []byte("beer"), err: nil},
	}
	sm := manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB()))

	userRepo, err := makeNewUserRepo()
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo()

	srv := &Server{
		IssuerURL:        url.URL{Scheme: "http", Host: "server.example.com"},
		KeyManager:       km,
		SessionManager:   sm,
		ClientRepo:       clientRepo,
		ClientManager:    clientManager,
		UserRepo:         userRepo,
		RefreshTokenRepo: refreshTokenRepo,
	}

	tests := []struct {
		scope        []string
		refreshToken string
	}{
		// No 'offline_access' in scope, should get empty refresh token.
		{
			scope:        []string{"openid"},
			refreshToken: "",
		},
		// Have 'offline_access' in scope, should get non-empty refresh token.
		{
			// NOTE(ericchiang): This test assumes that the database ID of the first
			// refresh token will be "1".
			scope:        []string{"openid", "offline_access"},
			refreshToken: fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
		},
	}

	for i, tt := range tests {
		sessionID, err := sm.NewSession("bogus_idpc", ci.Credentials.ID, "bogus", url.URL{}, "", false, tt.scope)
		if err != nil {
			t.Fatalf("case %d: unexpected error: %v", i, err)
		}
		_, err = sm.AttachRemoteIdentity(sessionID, oidc.Identity{})
		if err != nil {
			t.Fatalf("case %d: unexpected error: %v", i, err)
		}

		_, err = sm.AttachUser(sessionID, "testid-1")
		if err != nil {
			t.Fatalf("case %d: unexpected error: %v", i, err)
		}

		key, err := sm.NewSessionKey(sessionID)
		if err != nil {
			t.Fatalf("case %d: unexpected error: %v", i, err)
		}

		jwt, token, err := srv.CodeToken(ci.Credentials, key)
		if err != nil {
			t.Fatalf("case %d: unexpected error: %v", i, err)
		}
		if jwt == nil {
			t.Fatalf("case %d: expect non-nil jwt", i)
		}
		if token != tt.refreshToken {
			t.Fatalf("case %d: expect refresh token %q, got %q", i, tt.refreshToken, token)
		}
	}
}
Example #27
0
func (cfg *SingleServerConfig) Configure(srv *Server) error {
	k, err := key.GeneratePrivateKey()
	if err != nil {
		return err
	}

	dbMap := db.NewMemDB()

	ks := key.NewPrivateKeySet([]*key.PrivateKey{k}, time.Now().Add(24*time.Hour))
	kRepo := key.NewPrivateKeySetRepo()
	if err = kRepo.Set(ks); err != nil {
		return err
	}

	clients, err := loadClients(cfg.ClientsFile)
	if err != nil {
		return fmt.Errorf("unable to read clients from file %s: %v", cfg.ClientsFile, err)
	}

	clientRepo, err := db.NewClientRepoFromClients(dbMap, clients)
	if err != nil {
		return err
	}

	f, err := os.Open(cfg.ConnectorsFile)
	if err != nil {
		return fmt.Errorf("opening connectors file: %v", err)
	}
	defer f.Close()
	cfgs, err := connector.ReadConfigs(f)
	if err != nil {
		return fmt.Errorf("decoding connector configs: %v", err)
	}
	cfgRepo := db.NewConnectorConfigRepo(dbMap)
	if err := cfgRepo.Set(cfgs); err != nil {
		return fmt.Errorf("failed to set connectors: %v", err)
	}

	sRepo := db.NewSessionRepo(dbMap)
	skRepo := db.NewSessionKeyRepo(dbMap)
	sm := sessionmanager.NewSessionManager(sRepo, skRepo)

	users, pwis, err := loadUsers(cfg.UsersFile)
	if err != nil {
		return fmt.Errorf("unable to read users from file: %v", err)
	}
	userRepo, err := db.NewUserRepoFromUsers(dbMap, users)
	if err != nil {
		return err
	}

	pwiRepo, err := db.NewPasswordInfoRepoFromPasswordInfos(dbMap, pwis)
	if err != nil {
		return err
	}

	refTokRepo := db.NewRefreshTokenRepo(dbMap)

	txnFactory := db.TransactionFactory(dbMap)
	userManager := usermanager.NewUserManager(userRepo, pwiRepo, cfgRepo, txnFactory, usermanager.ManagerOptions{})
	clientManager := clientmanager.NewClientManager(clientRepo, db.TransactionFactory(dbMap), clientmanager.ManagerOptions{})
	if err != nil {
		return fmt.Errorf("Failed to create client identity manager: %v", err)
	}
	srv.ClientRepo = clientRepo
	srv.ClientManager = clientManager
	srv.KeySetRepo = kRepo
	srv.ConnectorConfigRepo = cfgRepo
	srv.UserRepo = userRepo
	srv.UserManager = userManager
	srv.PasswordInfoRepo = pwiRepo
	srv.SessionManager = sm
	srv.RefreshTokenRepo = refTokRepo
	srv.HealthChecks = append(srv.HealthChecks, db.NewHealthChecker(dbMap))
	srv.dbMap = dbMap
	return nil
}
Example #28
0
func TestServerTokenFail(t *testing.T) {
	issuerURL := url.URL{Scheme: "http", Host: "server.example.com"}
	keyFixture := "goodkey"
	ccFixture := oidc.ClientCredentials{
		ID:     testClientID,
		Secret: clientTestSecret,
	}
	signerFixture := &StaticSigner{sig: []byte("beer"), err: nil}

	tests := []struct {
		signer       jose.Signer
		argCC        oidc.ClientCredentials
		argKey       string
		err          error
		scope        []string
		refreshToken string
	}{
		// control test case to make sure fixtures check out
		{
			// NOTE(ericchiang): This test assumes that the database ID of the first
			// refresh token will be "1".
			signer:       signerFixture,
			argCC:        ccFixture,
			argKey:       keyFixture,
			scope:        []string{"openid", "offline_access"},
			refreshToken: fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
		},

		// no 'offline_access' in 'scope', should get empty refresh token
		{
			signer: signerFixture,
			argCC:  ccFixture,
			argKey: keyFixture,
			scope:  []string{"openid"},
		},

		// unrecognized key
		{
			signer: signerFixture,
			argCC:  ccFixture,
			argKey: "foo",
			err:    oauth2.NewError(oauth2.ErrorInvalidGrant),
			scope:  []string{"openid", "offline_access"},
		},

		// unrecognized client
		{
			signer: signerFixture,
			argCC:  oidc.ClientCredentials{ID: "YYY"},
			argKey: keyFixture,
			err:    oauth2.NewError(oauth2.ErrorInvalidClient),
			scope:  []string{"openid", "offline_access"},
		},

		// signing operation fails
		{
			signer: &StaticSigner{sig: nil, err: errors.New("fail")},
			argCC:  ccFixture,
			argKey: keyFixture,
			err:    oauth2.NewError(oauth2.ErrorServerError),
			scope:  []string{"openid", "offline_access"},
		},
	}

	for i, tt := range tests {
		sm := manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB()))
		sm.GenerateCode = func() (string, error) { return keyFixture, nil }

		sessionID, err := sm.NewSession("connector_id", ccFixture.ID, "bogus", url.URL{}, "", false, tt.scope)
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}

		_, err = sm.AttachRemoteIdentity(sessionID, oidc.Identity{})
		if err != nil {
			t.Errorf("case %d: unexpected error: %v", i, err)
			continue
		}
		km := &StaticKeyManager{
			signer: tt.signer,
		}

		clients := []client.Client{
			client.Client{
				Credentials: ccFixture,
				Metadata: oidc.ClientMetadata{
					RedirectURIs: []url.URL{
						validRedirURL,
					},
				},
			},
		}
		dbm := db.NewMemDB()
		clientIDGenerator := func(hostport string) (string, error) {
			return hostport, nil
		}
		secGen := func() ([]byte, error) {
			return []byte("secret"), nil
		}
		clientRepo := db.NewClientRepo(dbm)
		clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbm), clients, clientmanager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
		if err != nil {
			t.Fatalf("Failed to create client identity manager: %v", err)
		}
		_, err = sm.AttachUser(sessionID, "testid-1")
		if err != nil {
			t.Fatalf("case %d: unexpected error: %v", i, err)
		}

		userRepo, err := makeNewUserRepo()
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}

		refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo()

		srv := &Server{
			IssuerURL:        issuerURL,
			KeyManager:       km,
			SessionManager:   sm,
			ClientRepo:       clientRepo,
			ClientManager:    clientManager,
			UserRepo:         userRepo,
			RefreshTokenRepo: refreshTokenRepo,
		}

		_, err = sm.NewSessionKey(sessionID)
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}

		jwt, token, err := srv.CodeToken(tt.argCC, tt.argKey)
		if token != tt.refreshToken {
			fmt.Printf("case %d: expect refresh token %q, got %q\n", i, tt.refreshToken, token)
			t.Fatalf("case %d: expect refresh token %q, got %q", i, tt.refreshToken, token)
			panic("")
		}
		if !reflect.DeepEqual(err, tt.err) {
			t.Errorf("case %d: expect %v, got %v", i, tt.err, err)
		}
		if err == nil && jwt == nil {
			t.Errorf("case %d: got nil JWT", i)
		}
		if err != nil && jwt != nil {
			t.Errorf("case %d: got non-nil JWT %v", i, jwt)
		}
	}
}
Example #29
0
func TestServerRefreshToken(t *testing.T) {
	issuerURL := url.URL{Scheme: "http", Host: "server.example.com"}

	credXXX := oidc.ClientCredentials{
		ID:     "XXX",
		Secret: clientTestSecret,
	}
	credYYY := oidc.ClientCredentials{
		ID:     "YYY",
		Secret: clientTestSecret,
	}

	signerFixture := &StaticSigner{sig: []byte("beer"), err: nil}

	// NOTE(ericchiang): These tests assume that the database ID of the first
	// refresh token will be "1".
	tests := []struct {
		token    string
		clientID string // The client that associates with the token.
		creds    oidc.ClientCredentials
		signer   jose.Signer
		err      error
	}{
		// Everything is good.
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			"XXX",
			credXXX,
			signerFixture,
			nil,
		},
		// Invalid refresh token(malformatted).
		{
			"invalid-token",
			"XXX",
			credXXX,
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidRequest),
		},
		// Invalid refresh token(invalid payload content).
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-2"))),
			"XXX",
			credXXX,
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidRequest),
		},
		// Invalid refresh token(invalid ID content).
		{
			fmt.Sprintf("0/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			"XXX",
			credXXX,
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidRequest),
		},
		// Invalid client(client is not associated with the token).
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			"XXX",
			credYYY,
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidClient),
		},
		// Invalid client(no client ID).
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			"XXX",
			oidc.ClientCredentials{ID: "", Secret: "aaa"},
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidClient),
		},
		// Invalid client(no such client).
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			"XXX",
			oidc.ClientCredentials{ID: "AAA", Secret: "aaa"},
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidClient),
		},
		// Invalid client(no secrets).
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			"XXX",
			oidc.ClientCredentials{ID: "XXX"},
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidClient),
		},
		// Invalid client(invalid secret).
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			"XXX",
			oidc.ClientCredentials{ID: "XXX", Secret: "bad-secret"},
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidClient),
		},
		// Signing operation fails.
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			"XXX",
			credXXX,
			&StaticSigner{sig: nil, err: errors.New("fail")},
			oauth2.NewError(oauth2.ErrorServerError),
		},
	}

	for i, tt := range tests {
		km := &StaticKeyManager{
			signer: tt.signer,
		}

		ciRepo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{
			oidc.ClientIdentity{Credentials: credXXX},
			oidc.ClientIdentity{Credentials: credYYY},
		})
		if err != nil {
			t.Errorf("case %d: failed to create client identity repo: %v", i, err)
			continue
		}

		userRepo, err := makeNewUserRepo()
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}

		refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo()

		srv := &Server{
			IssuerURL:          issuerURL,
			KeyManager:         km,
			ClientIdentityRepo: ciRepo,
			UserRepo:           userRepo,
			RefreshTokenRepo:   refreshTokenRepo,
		}

		if _, err := refreshTokenRepo.Create("testid-1", tt.clientID); err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}

		jwt, err := srv.RefreshToken(tt.creds, tt.token)
		if !reflect.DeepEqual(err, tt.err) {
			t.Errorf("Case %d: expect: %v, got: %v", i, tt.err, err)
		}

		if jwt != nil {
			if string(jwt.Signature) != "beer" {
				t.Errorf("Case %d: expect signature: beer, got signature: %v", i, jwt.Signature)
			}
			claims, err := jwt.Claims()
			if err != nil {
				t.Errorf("Case %d: unexpected error: %v", i, err)
			}
			if claims["iss"] != issuerURL.String() || claims["sub"] != "testid-1" || claims["aud"] != "XXX" {
				t.Errorf("Case %d: invalid claims: %v", i, claims)
			}
		}
	}

	// Test that we should return error when user cannot be found after
	// verifying the token.
	km := &StaticKeyManager{
		signer: signerFixture,
	}

	ciRepo, err := db.NewClientIdentityRepoFromClients(db.NewMemDB(), []oidc.ClientIdentity{
		oidc.ClientIdentity{Credentials: credXXX},
		oidc.ClientIdentity{Credentials: credYYY},
	})
	if err != nil {
		t.Fatalf("failed to create client identity repo: %v", err)
	}

	userRepo, err := makeNewUserRepo()
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Create a user that will be removed later.
	if err := userRepo.Create(nil, user.User{
		ID:    "testid-2",
		Email: "*****@*****.**",
	}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo()

	srv := &Server{
		IssuerURL:          issuerURL,
		KeyManager:         km,
		ClientIdentityRepo: ciRepo,
		UserRepo:           userRepo,
		RefreshTokenRepo:   refreshTokenRepo,
	}

	if _, err := refreshTokenRepo.Create("testid-2", credXXX.ID); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Recreate the user repo to remove the user we created.
	userRepo, err = makeNewUserRepo()
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	srv.UserRepo = userRepo

	_, err = srv.RefreshToken(credXXX, fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))))
	if !reflect.DeepEqual(err, oauth2.NewError(oauth2.ErrorServerError)) {
		t.Errorf("Expect: %v, got: %v", oauth2.NewError(oauth2.ErrorServerError), err)
	}
}
Example #30
0
func TestServerRefreshToken(t *testing.T) {
	issuerURL := url.URL{Scheme: "http", Host: "server.example.com"}
	clientA := client.Client{
		Credentials: oidc.ClientCredentials{
			ID:     testClientID,
			Secret: clientTestSecret,
		},
		Metadata: oidc.ClientMetadata{
			RedirectURIs: []url.URL{
				url.URL{Scheme: "https", Host: "client.example.com", Path: "one/two/three"},
			},
		},
	}
	clientB := client.Client{
		Credentials: oidc.ClientCredentials{
			ID:     "example2.com",
			Secret: clientTestSecret,
		},
		Metadata: oidc.ClientMetadata{
			RedirectURIs: []url.URL{
				url.URL{Scheme: "https", Host: "example2.com", Path: "one/two/three"},
			},
		},
	}

	signerFixture := &StaticSigner{sig: []byte("beer"), err: nil}

	// NOTE(ericchiang): These tests assume that the database ID of the first
	// refresh token will be "1".
	tests := []struct {
		token    string
		clientID string // The client that associates with the token.
		creds    oidc.ClientCredentials
		signer   jose.Signer
		err      error
	}{
		// Everything is good.
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			clientA.Credentials.ID,
			clientA.Credentials,
			signerFixture,
			nil,
		},
		// Invalid refresh token(malformatted).
		{
			"invalid-token",
			clientA.Credentials.ID,
			clientA.Credentials,
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidRequest),
		},
		// Invalid refresh token(invalid payload content).
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-2"))),
			clientA.Credentials.ID,
			clientA.Credentials,
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidRequest),
		},
		// Invalid refresh token(invalid ID content).
		{
			fmt.Sprintf("0/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			clientA.Credentials.ID,
			clientA.Credentials,
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidRequest),
		},
		// Invalid client(client is not associated with the token).
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			clientA.Credentials.ID,
			clientB.Credentials,
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidClient),
		},
		// Invalid client(no client ID).
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			clientA.Credentials.ID,
			oidc.ClientCredentials{ID: "", Secret: "aaa"},
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidClient),
		},
		// Invalid client(no such client).
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			clientA.Credentials.ID,
			oidc.ClientCredentials{ID: "AAA", Secret: "aaa"},
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidClient),
		},
		// Invalid client(no secrets).
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			clientA.Credentials.ID,
			oidc.ClientCredentials{ID: testClientID},
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidClient),
		},
		// Invalid client(invalid secret).
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			clientA.Credentials.ID,
			oidc.ClientCredentials{ID: "bad-id", Secret: "bad-secret"},
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidClient),
		},
		// Signing operation fails.
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			clientA.Credentials.ID,
			clientA.Credentials,
			&StaticSigner{sig: nil, err: errors.New("fail")},
			oauth2.NewError(oauth2.ErrorServerError),
		},
	}

	for i, tt := range tests {
		km := &StaticKeyManager{
			signer: tt.signer,
		}

		clients := []client.Client{
			clientA,
			clientB,
		}

		clientIDGenerator := func(hostport string) (string, error) {
			return hostport, nil
		}
		secGen := func() ([]byte, error) {
			return []byte("secret"), nil
		}
		dbm := db.NewMemDB()
		clientRepo := db.NewClientRepo(dbm)
		clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbm), clients, clientmanager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
		if err != nil {
			t.Fatalf("Failed to create client identity manager: %v", err)
		}
		userRepo, err := makeNewUserRepo()
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}

		refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo()

		srv := &Server{
			IssuerURL:        issuerURL,
			KeyManager:       km,
			ClientRepo:       clientRepo,
			ClientManager:    clientManager,
			UserRepo:         userRepo,
			RefreshTokenRepo: refreshTokenRepo,
		}

		if _, err := refreshTokenRepo.Create("testid-1", tt.clientID); err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}

		jwt, err := srv.RefreshToken(tt.creds, tt.token)
		if !reflect.DeepEqual(err, tt.err) {
			t.Errorf("Case %d: expect: %v, got: %v", i, tt.err, err)
		}

		if jwt != nil {
			if string(jwt.Signature) != "beer" {
				t.Errorf("Case %d: expect signature: beer, got signature: %v", i, jwt.Signature)
			}
			claims, err := jwt.Claims()
			if err != nil {
				t.Errorf("Case %d: unexpected error: %v", i, err)
			}
			if claims["iss"] != issuerURL.String() || claims["sub"] != "testid-1" || claims["aud"] != testClientID {
				t.Errorf("Case %d: invalid claims: %v", i, claims)
			}
		}
	}

	// Test that we should return error when user cannot be found after
	// verifying the token.
	km := &StaticKeyManager{
		signer: signerFixture,
	}

	clients := []client.Client{
		clientA,
		clientB,
	}
	clientIDGenerator := func(hostport string) (string, error) {
		return hostport, nil
	}
	secGen := func() ([]byte, error) {
		return []byte("secret"), nil
	}
	dbm := db.NewMemDB()
	clientRepo := db.NewClientRepo(dbm)
	clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbm), clients, clientmanager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
	if err != nil {
		t.Fatalf("Failed to create client identity manager: %v", err)
	}
	userRepo, err := makeNewUserRepo()
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Create a user that will be removed later.
	if err := userRepo.Create(nil, user.User{
		ID:    "testid-2",
		Email: "*****@*****.**",
	}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo()

	srv := &Server{
		IssuerURL:        issuerURL,
		KeyManager:       km,
		ClientRepo:       clientRepo,
		ClientManager:    clientManager,
		UserRepo:         userRepo,
		RefreshTokenRepo: refreshTokenRepo,
	}

	if _, err := refreshTokenRepo.Create("testid-2", clientA.Credentials.ID); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Recreate the user repo to remove the user we created.
	userRepo, err = makeNewUserRepo()
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	srv.UserRepo = userRepo

	_, err = srv.RefreshToken(clientA.Credentials, fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))))
	if !reflect.DeepEqual(err, oauth2.NewError(oauth2.ErrorServerError)) {
		t.Errorf("Expect: %v, got: %v", oauth2.NewError(oauth2.ErrorServerError), err)
	}
}