Beispiel #1
0
func getDriver() (drv driver) {
	var err error
	switch {
	case len(global.dbURL) > 0:
		drv, err = newDBDriver(global.dbURL)
	case len(global.endpoint) > 0:
		if len(global.creds.ID) == 0 || len(global.creds.Secret) == 0 {
			err = errors.New("--client-id/--client-secret flags unset")
		} else {
			pcfg, err := oidc.FetchProviderConfig(http.DefaultClient, global.endpoint)
			if err != nil {
				stderr("Unable to fetch provider config: %v", err)
				os.Exit(1)
			}
			drv, err = newAPIDriver(pcfg, global.creds)
		}
	default:
		err = errors.New("--endpoint/--db-url flags unset")
	}

	if err != nil {
		stderr("Unable to configure dexctl driver: %v", err)
		os.Exit(1)
	}

	return
}
Beispiel #2
0
func (op *OIDCProvider) DiscoverConfig() (config *oidc.ProviderConfig, shouldSync bool, err error) {

	// If discovery URI is explicitly defined, use it instead of the standard issuer-based discovery.

	if op.DiscoveryURI != "" || op.DisableConfigValidation {
		config, err = op.FetchCustomProviderConfig(op.DiscoveryURI)
		shouldSync = false
	} else {

		var standardConfig oidc.ProviderConfig
		maxRetryAttempts := 5
		for i := 1; i <= maxRetryAttempts; i++ {
			standardConfig, err = oidc.FetchProviderConfig(http.DefaultClient, op.Issuer)
			if err == nil {
				config = &standardConfig
				shouldSync = true
				break
			}
			base.LogTo("OIDC+", "Unable to fetch provider config from discovery endpoint for %s (attempt %v/%v): %v",
				op.Issuer, i, maxRetryAttempts, err)
			time.Sleep(500 * time.Millisecond)
		}
	}

	return config, shouldSync, err
}
Beispiel #3
0
func (a *OIDCAuthenticator) client() (*oidc.Client, error) {
	// Fast check to see if client has already been initialized.
	if client := a.oidcClient.Load(); client != nil {
		return client.(*oidc.Client), nil
	}

	// Acquire lock, then recheck initialization.
	a.mu.Lock()
	defer a.mu.Unlock()
	if client := a.oidcClient.Load(); client != nil {
		return client.(*oidc.Client), nil
	}

	// Try to initialize client.
	providerConfig, err := oidc.FetchProviderConfig(a.httpClient, strings.TrimSuffix(a.issuerURL, "/"))
	if err != nil {
		glog.Errorf("oidc authenticator: failed to fetch provider discovery data: %v", err)
		return nil, fmt.Errorf("fetch provider config: %v", err)
	}

	clientConfig := oidc.ClientConfig{
		HTTPClient:     a.httpClient,
		Credentials:    oidc.ClientCredentials{ID: a.trustedClientID},
		ProviderConfig: providerConfig,
	}

	client, err := oidc.NewClient(clientConfig)
	if err != nil {
		glog.Errorf("oidc authenticator: failed to create client: %v", err)
		return nil, fmt.Errorf("create client: %v", err)
	}

	// SyncProviderConfig will start a goroutine to periodically synchronize the provider config.
	// The synchronization interval is set by the expiration length of the config, and has a mininum
	// and maximum threshold.
	stop := client.SyncProviderConfig(a.issuerURL)
	a.oidcClient.Store(client)
	a.close = func() {
		// This assumes the stop is an unbuffered channel.
		// So instead of closing the channel, we send am empty struct here.
		// This guarantees that when this function returns, there is no flying requests,
		// because a send to an unbuffered channel happens after the receive from the channel.
		stop <- struct{}{}
	}
	return client, nil
}
Beispiel #4
0
func NewClient(clientID, clientSecret, discovery, redirectURL string, tlsConfig *tls.Config) (*Client, *ClientCredsTokenManager) {
	cc := oidc.ClientCredentials{
		ID:     clientID,
		Secret: clientSecret,
	}
	httpClient := &http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}}

	var cfg oidc.ProviderConfig
	var err error

	for {
		cfg, err = oidc.FetchProviderConfig(httpClient, discovery)
		if err == nil {
			break
		}

		sleep := 3 * time.Second
		log.Errorf("Failed fetching provider config, trying again in %v: %v", sleep, err)
		time.Sleep(sleep)
	}

	log.Infof("Fetched provider config from %s", discovery)

	ccfg := oidc.ClientConfig{
		HTTPClient:     httpClient,
		ProviderConfig: cfg,
		Credentials:    cc,
		RedirectURL:    redirectURL,
	}

	client, err := NewOIDCClient(ccfg)
	if err != nil {
		log.Fatalf("Unable to create Client: %v", err)
	}

	client.SyncProviderConfig(discovery)

	tm := NewClientCredsTokenManager(client, discovery)
	tm.Run()
	tm.WaitUntilInitialSync()

	return client, tm
}
Beispiel #5
0
func NewOIDCClient(id, secret, discovery string) (*Client, error) {
	var cfg oidc.ProviderConfig
	var err error
	for {
		cfg, err = oidc.FetchProviderConfig(http.DefaultClient, discovery)
		if err == nil {
			break
		}
		sleep := 1 * time.Second
		fmt.Printf("Failed fetching provider config, trying again in %v: %v\n", sleep, err)
		time.Sleep(sleep)
	}

	c := Client{
		providerConfig: newProviderConfigRepo(cfg),
		httpClient:     http.DefaultClient,
	}

	return &c, nil
}
Beispiel #6
0
func mockClient(srv *server.Server, ci client.Client) (*oidc.Client, error) {
	hdlr := srv.HTTPHandler()
	sClient := &phttp.HandlerClient{Handler: hdlr}

	cfg, err := oidc.FetchProviderConfig(sClient, srv.IssuerURL.String())
	if err != nil {
		return nil, fmt.Errorf("failed to fetch provider config: %v", err)
	}

	jwks, err := srv.KeyManager.JWKs()
	if err != nil {
		return nil, fmt.Errorf("failed to generate JWKs: %v", err)
	}

	ks := key.NewPublicKeySet(jwks, time.Now().Add(1*time.Hour))
	ccfg := oidc.ClientConfig{
		HTTPClient:     sClient,
		ProviderConfig: cfg,
		Credentials:    ci.Credentials,
		KeySet:         *ks,
	}

	return oidc.NewClient(ccfg)
}
Beispiel #7
0
// New creates a new OpenID Connect client with the given issuerURL and clientID.
// NOTE(yifan): For now we assume the server provides the "jwks_uri" so we don't
// need to manager the key sets by ourselves.
func New(issuerURL, clientID, caFile, usernameClaim string) (*OIDCAuthenticator, error) {
	var cfg oidc.ProviderConfig
	var err error
	var roots *x509.CertPool

	url, err := url.Parse(issuerURL)
	if err != nil {
		return nil, err
	}

	if url.Scheme != "https" {
		return nil, fmt.Errorf("'oidc-issuer-url' (%q) has invalid scheme (%q), require 'https'", issuerURL, url.Scheme)
	}

	if caFile != "" {
		roots, err = util.CertPoolFromFile(caFile)
		if err != nil {
			glog.Errorf("Failed to read the CA file: %v", err)
		}
	}
	if roots == nil {
		glog.Info("No x509 certificates provided, will use host's root CA set")
	}

	// Copied from http.DefaultTransport.
	tr := &http.Transport{
		// According to golang's doc, if RootCAs is nil,
		// TLS uses the host's root CA set.
		TLSClientConfig: &tls.Config{RootCAs: roots},
		Proxy:           http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,
	}

	hc := &http.Client{}
	hc.Transport = tr

	for i := 0; i <= maxRetries; i++ {
		if i == maxRetries {
			return nil, fmt.Errorf("failed to fetch provider config after %v retries", maxRetries)
		}

		cfg, err = oidc.FetchProviderConfig(hc, issuerURL)
		if err == nil {
			break
		}
		glog.Errorf("Failed to fetch provider config, trying again in %v: %v", retryBackoff, err)
		time.Sleep(retryBackoff)
	}

	glog.Infof("Fetched provider config from %s: %#v", issuerURL, cfg)

	if cfg.KeysEndpoint == "" {
		return nil, fmt.Errorf("OIDC provider must provide 'jwks_uri' for public key discovery")
	}

	ccfg := oidc.ClientConfig{
		HTTPClient:     hc,
		Credentials:    oidc.ClientCredentials{ID: clientID},
		ProviderConfig: cfg,
	}

	client, err := oidc.NewClient(ccfg)
	if err != nil {
		return nil, err
	}

	// SyncProviderConfig will start a goroutine to periodically synchronize the provider config.
	// The synchronization interval is set by the expiration length of the config, and has a mininum
	// and maximum threshold.
	client.SyncProviderConfig(issuerURL)

	return &OIDCAuthenticator{ccfg, client, usernameClaim}, nil
}
Beispiel #8
0
func newOIDCAuthProvider(_ string, cfg map[string]string, persister rest.AuthProviderConfigPersister) (rest.AuthProvider, error) {
	issuer := cfg[cfgIssuerUrl]
	if issuer == "" {
		return nil, fmt.Errorf("Must provide %s", cfgIssuerUrl)
	}

	clientID := cfg[cfgClientID]
	if clientID == "" {
		return nil, fmt.Errorf("Must provide %s", cfgClientID)
	}

	clientSecret := cfg[cfgClientSecret]
	if clientSecret == "" {
		return nil, fmt.Errorf("Must provide %s", cfgClientSecret)
	}

	var certAuthData []byte
	var err error
	if cfg[cfgCertificateAuthorityData] != "" {
		certAuthData, err = base64.StdEncoding.DecodeString(cfg[cfgCertificateAuthorityData])
		if err != nil {
			return nil, err
		}
	}

	clientConfig := rest.Config{
		TLSClientConfig: rest.TLSClientConfig{
			CAFile: cfg[cfgCertificateAuthority],
			CAData: certAuthData,
		},
	}

	trans, err := rest.TransportFor(&clientConfig)
	if err != nil {
		return nil, err
	}
	hc := &http.Client{Transport: trans}

	providerCfg, err := oidc.FetchProviderConfig(hc, issuer)
	if err != nil {
		return nil, fmt.Errorf("error fetching provider config: %v", err)
	}

	scopes := strings.Split(cfg[cfgExtraScopes], ",")
	oidcCfg := oidc.ClientConfig{
		HTTPClient: hc,
		Credentials: oidc.ClientCredentials{
			ID:     clientID,
			Secret: clientSecret,
		},
		ProviderConfig: providerCfg,
		Scope:          append(scopes, oidc.DefaultScope...),
	}

	client, err := oidc.NewClient(oidcCfg)
	if err != nil {
		return nil, fmt.Errorf("error creating OIDC Client: %v", err)
	}

	oClient := &oidcClient{client}

	var initialIDToken jose.JWT
	if cfg[cfgIDToken] != "" {
		initialIDToken, err = jose.ParseJWT(cfg[cfgIDToken])
		if err != nil {
			return nil, err
		}
	}

	return &oidcAuthProvider{
		initialIDToken: initialIDToken,
		refresher: &idTokenRefresher{
			client:    oClient,
			cfg:       cfg,
			persister: persister,
		},
	}, nil
}
Beispiel #9
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)
	}
}
Beispiel #10
0
func main() {
	fs := flag.NewFlagSet("oidc-app", flag.ExitOnError)
	listen := fs.String("listen", "http://127.0.0.1:5555", "")
	redirectURL := fs.String("redirect-url", "http://127.0.0.1:5555/callback", "")
	clientID := fs.String("client-id", "", "")
	clientSecret := fs.String("client-secret", "", "")
	caFile := fs.String("trusted-ca-file", "", "the TLS CA file, if empty then the host's root CA will be used")

	discovery := fs.String("discovery", "https://accounts.google.com", "")
	logDebug := fs.Bool("log-debug", false, "log debug-level information")
	logTimestamps := fs.Bool("log-timestamps", false, "prefix log lines with timestamps")

	if err := fs.Parse(os.Args[1:]); err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	if err := pflag.SetFlagsFromEnv(fs, "EXAMPLE_APP"); err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	if *logDebug {
		log.EnableDebug()
	}
	if *logTimestamps {
		log.EnableTimestamps()
	}

	if *clientID == "" {
		log.Fatal("--client-id must be set")
	}

	if *clientSecret == "" {
		log.Fatal("--client-secret must be set")
	}

	l, err := url.Parse(*listen)
	if err != nil {
		log.Fatalf("Unable to use --listen flag: %v", err)
	}

	_, p, err := net.SplitHostPort(l.Host)
	if err != nil {
		log.Fatalf("Unable to parse host from --listen flag: %v", err)
	}

	cc := oidc.ClientCredentials{
		ID:     *clientID,
		Secret: *clientSecret,
	}

	var tlsConfig tls.Config
	if *caFile != "" {
		roots := x509.NewCertPool()
		pemBlock, err := ioutil.ReadFile(*caFile)
		if err != nil {
			log.Fatalf("Unable to read ca file: %v", err)
		}
		roots.AppendCertsFromPEM(pemBlock)
		tlsConfig.RootCAs = roots
	}

	httpClient := &http.Client{Transport: &http.Transport{TLSClientConfig: &tlsConfig}}

	var cfg oidc.ProviderConfig
	for {
		cfg, err = oidc.FetchProviderConfig(httpClient, *discovery)
		if err == nil {
			break
		}

		sleep := 3 * time.Second
		log.Errorf("Failed fetching provider config, trying again in %v: %v", sleep, err)
		time.Sleep(sleep)
	}

	log.Infof("Fetched provider config from %s: %#v", *discovery, cfg)

	ccfg := oidc.ClientConfig{
		HTTPClient:     httpClient,
		ProviderConfig: cfg,
		Credentials:    cc,
		RedirectURL:    *redirectURL,
	}

	client, err := oidc.NewClient(ccfg)
	if err != nil {
		log.Fatalf("Unable to create Client: %v", err)
	}

	client.SyncProviderConfig(*discovery)

	redirectURLParsed, err := url.Parse(*redirectURL)
	if err != nil {
		log.Fatalf("Unable to parse url from --redirect-url flag: %v", err)
	}
	hdlr := NewClientHandler(client, *discovery, *redirectURLParsed)
	httpsrv := &http.Server{
		Addr:    fmt.Sprintf(":%s", p),
		Handler: hdlr,
	}

	log.Infof("Binding to %s...", httpsrv.Addr)
	log.Fatal(httpsrv.ListenAndServe())
}
Beispiel #11
0
func main() {
	fs := flag.NewFlagSet("oidc-example-cli", flag.ExitOnError)
	clientID := fs.String("client-id", "", "")
	clientSecret := fs.String("client-secret", "", "")
	discovery := fs.String("discovery", "https://accounts.google.com", "")

	if err := fs.Parse(os.Args[1:]); err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	if *clientID == "" {
		fmt.Println("--client-id must be set")
		os.Exit(2)
	}

	if *clientSecret == "" {
		fmt.Println("--client-secret must be set")
		os.Exit(2)
	}

	cc := oidc.ClientCredentials{
		ID:     *clientID,
		Secret: *clientSecret,
	}

	fmt.Printf("fetching provider config from %s...", *discovery)

	// NOTE: A real CLI would cache this config, or provide it via flags/config file.
	var cfg oidc.ProviderConfig
	var err error
	for {
		cfg, err = oidc.FetchProviderConfig(http.DefaultClient, *discovery)
		if err == nil {
			break
		}

		sleep := 1 * time.Second
		fmt.Printf("failed fetching provider config, trying again in %v: %v\n", sleep, err)
		time.Sleep(sleep)
	}

	fmt.Printf("fetched provider config from %s: %#v\n\n", *discovery, cfg)

	ccfg := oidc.ClientConfig{
		ProviderConfig: cfg,
		Credentials:    cc,
	}

	client, err := oidc.NewClient(ccfg)
	if err != nil {
		fmt.Printf("unable to create Client: %v\n", err)
		os.Exit(1)
	}

	tok, err := client.ClientCredsToken([]string{"openid"})
	if err != nil {
		fmt.Printf("unable to verify auth code with issuer: %v\n", err)
		os.Exit(1)
	}

	fmt.Printf("got jwt: %v\n\n", tok.Encode())

	claims, err := tok.Claims()
	if err != nil {
		fmt.Printf("unable to construct claims: %v\n", err)
		os.Exit(1)
	}

	fmt.Printf("got claims %#v...\n", claims)
}
Beispiel #12
0
func main() {
	fs := flag.NewFlagSet("example-cli", flag.ExitOnError)
	clientID := fs.String("client-id", "", "")
	clientSecret := fs.String("client-secret", "", "")
	discovery := fs.String("discovery", "http://localhost:5556", "")
	logDebug := fs.Bool("log-debug", false, "log debug-level information")
	logTimestamps := fs.Bool("log-timestamps", false, "prefix log lines with timestamps")

	if err := fs.Parse(os.Args[1:]); err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	if err := pflag.SetFlagsFromEnv(fs, "EXAMPLE_CLI"); err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	if *logDebug {
		log.EnableDebug()
	}
	if *logTimestamps {
		log.EnableTimestamps()
	}

	if *clientID == "" {
		fmt.Println("--client-id must be set")
		os.Exit(2)
	}

	if *clientSecret == "" {
		fmt.Println("--client-secret must be set")
		os.Exit(2)
	}

	cc := oidc.ClientCredentials{
		ID:     *clientID,
		Secret: *clientSecret,
	}

	// NOTE: A real CLI would cache this config, or provide it via flags/config file.
	var cfg oidc.ProviderConfig
	var err error
	for {
		cfg, err = oidc.FetchProviderConfig(http.DefaultClient, *discovery)
		if err == nil {
			break
		}

		sleep := 1 * time.Second
		fmt.Printf("Failed fetching provider config, trying again in %v: %v\n", sleep, err)
		time.Sleep(sleep)
	}

	fmt.Printf("Fetched provider config from %s: %#v\n\n", *discovery, cfg)

	ccfg := oidc.ClientConfig{
		ProviderConfig: cfg,
		Credentials:    cc,
	}

	client, err := oidc.NewClient(ccfg)
	if err != nil {
		log.Fatalf("Unable to create Client: %v", err)
	}

	tok, err := client.ClientCredsToken([]string{"openid"})
	if err != nil {
		fmt.Printf("unable to verify auth code with issuer: %v\n", err)
		os.Exit(1)
	}

	fmt.Printf("got jwt: %v\n\n", tok.Encode())

	claims, err := tok.Claims()
	if err != nil {
		fmt.Printf("unable to construct claims: %v\n", err)
		os.Exit(1)
	}

	fmt.Printf("got claims %#v...\n", claims)
}
Beispiel #13
0
func main() {
	log.SetOutput(os.Stderr)

	fs := flag.NewFlagSet("oidc-example-app", flag.ExitOnError)
	listen := fs.String("listen", defaultListenHost, "serve traffic on this address (<host>:<port>)")
	redirectURL := fs.String("redirect-url", fmt.Sprintf("http://%s%s", defaultListenHost, pathCallback), "")
	clientID := fs.String("client-id", "", "")
	clientSecret := fs.String("client-secret", "", "")
	discovery := fs.String("discovery", "https://accounts.google.com", "")

	if err := fs.Parse(os.Args[1:]); err != nil {
		log.Fatalf("failed parsing flags: %v", err)
	}

	if *clientID == "" {
		log.Fatal("--client-id must be set")
	}

	if *clientSecret == "" {
		log.Fatal("--client-secret must be set")
	}

	_, _, err := net.SplitHostPort(*listen)
	if err != nil {
		log.Fatalf("unable to parse host:port from --listen flag: %v", err)
	}

	cc := oidc.ClientCredentials{
		ID:     *clientID,
		Secret: *clientSecret,
	}

	log.Printf("fetching provider config from %s...", *discovery)

	var cfg oidc.ProviderConfig
	for {
		cfg, err = oidc.FetchProviderConfig(http.DefaultClient, *discovery)
		if err == nil {
			break
		}

		sleep := 3 * time.Second
		log.Printf("failed fetching provider config, trying again in %v: %v", sleep, err)
		time.Sleep(sleep)
	}

	log.Printf("fetched provider config from %s: %#v", *discovery, cfg)

	ccfg := oidc.ClientConfig{
		ProviderConfig: cfg,
		Credentials:    cc,
		RedirectURL:    *redirectURL,
	}

	client, err := oidc.NewClient(ccfg)
	if err != nil {
		log.Fatalf("unable to create Client: %v", err)
	}

	client.SyncProviderConfig(*discovery)

	redirectURLParsed, err := url.Parse(*redirectURL)
	if err != nil {
		log.Fatalf("unable to parse url from --redirect-url flag: %v", err)
	}
	hdlr := NewClientHandler(client, *redirectURLParsed)
	httpsrv := &http.Server{
		Addr:    fmt.Sprintf(*listen),
		Handler: hdlr,
	}

	log.Printf("binding to %s...", httpsrv.Addr)
	log.Fatal(httpsrv.ListenAndServe())
}
Beispiel #14
0
// New creates a new OpenID Connect client with the given issuerURL and clientID.
// NOTE(yifan): For now we assume the server provides the "jwks_uri" so we don't
// need to manager the key sets by ourselves.
func New(opts OIDCOptions) (*OIDCAuthenticator, error) {
	var cfg oidc.ProviderConfig
	var err error
	var roots *x509.CertPool

	url, err := url.Parse(opts.IssuerURL)
	if err != nil {
		return nil, err
	}

	if url.Scheme != "https" {
		return nil, fmt.Errorf("'oidc-issuer-url' (%q) has invalid scheme (%q), require 'https'", opts.IssuerURL, url.Scheme)
	}

	if opts.CAFile != "" {
		roots, err = crypto.CertPoolFromFile(opts.CAFile)
		if err != nil {
			glog.Errorf("Failed to read the CA file: %v", err)
		}
	}
	if roots == nil {
		glog.Info("No x509 certificates provided, will use host's root CA set")
	}

	// Copied from http.DefaultTransport.
	tr := net.SetTransportDefaults(&http.Transport{
		// According to golang's doc, if RootCAs is nil,
		// TLS uses the host's root CA set.
		TLSClientConfig: &tls.Config{RootCAs: roots},
	})

	hc := &http.Client{}
	hc.Transport = tr

	maxRetries := opts.MaxRetries
	if maxRetries < 0 {
		maxRetries = DefaultRetries
	}
	retryBackoff := opts.RetryBackoff
	if retryBackoff < 0 {
		retryBackoff = DefaultBackoff
	}

	for i := 0; i <= maxRetries; i++ {
		if i == maxRetries {
			return nil, fmt.Errorf("failed to fetch provider config after %v retries", maxRetries)
		}

		cfg, err = oidc.FetchProviderConfig(hc, strings.TrimSuffix(opts.IssuerURL, "/"))
		if err == nil {
			break
		}
		glog.Errorf("Failed to fetch provider config, trying again in %v: %v", retryBackoff, err)
		time.Sleep(retryBackoff)
	}

	glog.Infof("Fetched provider config from %s: %#v", opts.IssuerURL, cfg)

	ccfg := oidc.ClientConfig{
		HTTPClient:     hc,
		Credentials:    oidc.ClientCredentials{ID: opts.ClientID},
		ProviderConfig: cfg,
	}

	client, err := oidc.NewClient(ccfg)
	if err != nil {
		return nil, err
	}

	// SyncProviderConfig will start a goroutine to periodically synchronize the provider config.
	// The synchronization interval is set by the expiration length of the config, and has a mininum
	// and maximum threshold.
	stop := client.SyncProviderConfig(opts.IssuerURL)

	return &OIDCAuthenticator{
		ccfg,
		client,
		opts.UsernameClaim,
		opts.GroupsClaim,
		stop,
		maxRetries,
		retryBackoff,
	}, nil
}
Beispiel #15
0
func NewOIDCProvider(p *ProviderData) *OIDCProvider {
	var err error

	p.ProviderName = "OpenID Connect"

	cc := oidc.ClientCredentials{
		ID:     p.ClientID,
		Secret: p.ClientSecret,
	}

	var tlsConfig tls.Config
	// TODO: do handling of custom certs
	httpClient := &http.Client{Transport: &http.Transport{TLSClientConfig: &tlsConfig}}

	var cfg oidc.ProviderConfig
	for {
		cfg, err = oidc.FetchProviderConfig(httpClient, p.DiscoveryURL.String())
		if err == nil {
			break
		}

		sleep := 3 * time.Second
		log.Printf("Failed fetching provider config, trying again in %v: %v", sleep, err)
		time.Sleep(sleep)
	}

	u, err := url.Parse(cfg.TokenEndpoint)
	if err != nil {
		panic(err)
	}
	p.ValidateURL = u
	u, err = url.Parse(cfg.AuthEndpoint)
	if err != nil {
		panic(err)
	}
	p.RedeemURL = u
	p.Scope = "email"

	ccfg := oidc.ClientConfig{
		HTTPClient:     httpClient,
		ProviderConfig: cfg,
		Credentials:    cc,
	}

	client, err := oidc.NewClient(ccfg)
	if err != nil {
		log.Fatalf("Unable to create Client: %v", err)
	}

	client.SyncProviderConfig(p.DiscoveryURL.String())

	oac, err := client.OAuthClient()
	if err != nil {
		panic("unable to proceed")
	}

	login, err := url.Parse(oac.AuthCodeURL("", "", ""))
	if err != nil {
		panic("unable to proceed")
	}

	p.LoginURL = login

	return &OIDCProvider{
		ProviderData: p,
		clientConfig: ccfg,
	}
}
Beispiel #16
0
func newOIDCAuthProvider(_ string, cfg map[string]string, persister restclient.AuthProviderConfigPersister) (restclient.AuthProvider, error) {
	issuer := cfg[cfgIssuerUrl]
	if issuer == "" {
		return nil, fmt.Errorf("Must provide %s", cfgIssuerUrl)
	}

	clientID := cfg[cfgClientID]
	if clientID == "" {
		return nil, fmt.Errorf("Must provide %s", cfgClientID)
	}

	clientSecret := cfg[cfgClientSecret]
	if clientSecret == "" {
		return nil, fmt.Errorf("Must provide %s", cfgClientSecret)
	}

	// Check cache for existing provider.
	if provider, ok := cache.getClient(issuer, clientID, clientSecret); ok {
		return provider, nil
	}

	var certAuthData []byte
	var err error
	if cfg[cfgCertificateAuthorityData] != "" {
		certAuthData, err = base64.StdEncoding.DecodeString(cfg[cfgCertificateAuthorityData])
		if err != nil {
			return nil, err
		}
	}

	clientConfig := restclient.Config{
		TLSClientConfig: restclient.TLSClientConfig{
			CAFile: cfg[cfgCertificateAuthority],
			CAData: certAuthData,
		},
	}

	trans, err := restclient.TransportFor(&clientConfig)
	if err != nil {
		return nil, err
	}
	hc := &http.Client{Transport: trans}

	providerCfg, err := oidc.FetchProviderConfig(hc, issuer)
	if err != nil {
		return nil, fmt.Errorf("error fetching provider config: %v", err)
	}

	scopes := strings.Split(cfg[cfgExtraScopes], ",")
	oidcCfg := oidc.ClientConfig{
		HTTPClient: hc,
		Credentials: oidc.ClientCredentials{
			ID:     clientID,
			Secret: clientSecret,
		},
		ProviderConfig: providerCfg,
		Scope:          append(scopes, oidc.DefaultScope...),
	}
	client, err := oidc.NewClient(oidcCfg)
	if err != nil {
		return nil, fmt.Errorf("error creating OIDC Client: %v", err)
	}

	provider := &oidcAuthProvider{
		client:    &oidcClient{client},
		cfg:       cfg,
		persister: persister,
		now:       time.Now,
	}

	return cache.setClient(issuer, clientID, clientSecret, provider), nil
}
Beispiel #17
0
func main() {
	fs := flag.NewFlagSet("oidc-app", flag.ExitOnError)
	listen := fs.String("listen", "http://127.0.0.1:5555", "")
	redirectURL := fs.String("redirect-url", "http://127.0.0.1:5555/callback", "")
	clientID := fs.String("client-id", "example-app", "")
	clientSecret := fs.String("client-secret", "ZXhhbXBsZS1hcHAtc2VjcmV0", "")
	caFile := fs.String("trusted-ca-file", "", "the TLS CA file, if empty then the host's root CA will be used")

	certFile := fs.String("tls-cert-file", "", "the TLS cert file. If empty, the app will listen on HTTP")
	keyFile := fs.String("tls-key-file", "", "the TLS key file. If empty, the app will listen on HTTP")

	discovery := fs.String("discovery", "http://127.0.0.1:5556", "")
	logDebug := fs.Bool("log-debug", false, "log debug-level information")
	logTimestamps := fs.Bool("log-timestamps", false, "prefix log lines with timestamps")

	if err := fs.Parse(os.Args[1:]); err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	if err := pflag.SetFlagsFromEnv(fs, "EXAMPLE_APP"); err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	if *logDebug {
		log.EnableDebug()
	}
	if *logTimestamps {
		log.EnableTimestamps()
	}

	if *clientID == "" {
		log.Fatal("--client-id must be set")
	}

	if *clientSecret == "" {
		log.Fatal("--client-secret must be set")
	}

	l, err := url.Parse(*listen)
	if err != nil {
		log.Fatalf("Unable to use --listen flag: %v", err)
	}

	_, p, err := net.SplitHostPort(l.Host)
	if err != nil {
		log.Fatalf("Unable to parse host from --listen flag: %v", err)
	}

	redirectURLParsed, err := url.Parse(*redirectURL)
	if err != nil {
		log.Fatalf("Unable to parse url from --redirect-url flag: %v", err)
	}

	useTLS := *keyFile != "" && *certFile != ""
	if useTLS && (redirectURLParsed.Scheme != "https" || l.Scheme != "https") {
		log.Fatalf(`TLS Cert File and Key File were provided. Ensure listen and redirect URLs are using the "https://" scheme.`)
	}

	cc := oidc.ClientCredentials{
		ID:     *clientID,
		Secret: *clientSecret,
	}

	var tlsConfig tls.Config
	if *caFile != "" {
		roots := x509.NewCertPool()
		pemBlock, err := ioutil.ReadFile(*caFile)
		if err != nil {
			log.Fatalf("Unable to read ca file: %v", err)
		}
		roots.AppendCertsFromPEM(pemBlock)
		tlsConfig.RootCAs = roots
	}

	httpClient := &http.Client{Transport: &http.Transport{TLSClientConfig: &tlsConfig}}

	var cfg oidc.ProviderConfig
	for {
		cfg, err = oidc.FetchProviderConfig(httpClient, *discovery)
		if err == nil {
			break
		}

		sleep := 3 * time.Second
		log.Errorf("Failed fetching provider config, trying again in %v: %v", sleep, err)
		time.Sleep(sleep)
	}

	log.Infof("Fetched provider config from %s: %#v", *discovery, cfg)

	ccfg := oidc.ClientConfig{
		HTTPClient:     httpClient,
		ProviderConfig: cfg,
		Credentials:    cc,
		RedirectURL:    *redirectURL,
		Scope:          append(oidc.DefaultScope, "offline_access"),
	}

	client, err := oidc.NewClient(ccfg)
	if err != nil {
		log.Fatalf("Unable to create Client: %v", err)
	}

	client.SyncProviderConfig(*discovery)

	hdlr := NewClientHandler(client, *discovery, *redirectURLParsed)
	httpsrv := &http.Server{
		Addr:    fmt.Sprintf(":%s", p),
		Handler: hdlr,
	}

	indexBytes, err := Asset("data/index.html")
	if err != nil {
		log.Fatalf("could not load template: %q", err)
	}

	indexTemplate = template.Must(template.New("root").Parse(string(indexBytes)))

	log.Infof("Binding to %s...", httpsrv.Addr)
	if useTLS {
		log.Info("Key and cert file provided. Using TLS")
		log.Fatal(httpsrv.ListenAndServeTLS(*certFile, *keyFile))
	} else {
		log.Fatal(httpsrv.ListenAndServe())
	}
}
Beispiel #18
0
func connectClient(connect2registry, connect2catalog, connect2api bool) {

	discovery := os.Getenv("DEX_WORKER_ISSUER")

	cfg, err := oidc.FetchProviderConfig(http.DefaultClient, discovery)
	if err != nil {
		logrus.Fatalln("Unable to fetch provider configs")
	}

	ccfg := oidc.ClientConfig{
		ProviderConfig: cfg,
		Credentials:    cc,
	}

	//logrus.Printf("ccfg.Credentials: %+v",ccfg.Credentials)
	//logrus.Printf("ccfg.ProviderConfig: %+v",ccfg.ProviderConfig)

	myclient, err := oidc.NewClient(ccfg)
	if err != nil {
		logrus.Fatalf("Unable to create Client: %v", err)
	}

	tok, err := myclient.ClientCredsToken([]string{"openid"})
	if err != nil {
		logrus.Printf("Token: %+v ", tok)
		logrus.Errorf("Error: %+v", err)
		logrus.Fatalf("Unable to get token")
	}

	jwtCreds := NewOauthAccess(tok.Encode())
	//logrus.Println("got the jwtCreds")
	claims, err := tok.Claims()
	clientsub, _, _ = claims.StringClaim("sub")
	//logrus.Println("Get the clientsub", clientsub)

	if err != nil {
		logrus.Fatalln("unable to get getClientIdAndEmail", err)
	}

	if len(cafile) > 0 {
		auth, err := credentials.NewClientTLSFromFile(cafile, "")
		if err != nil {
			panic(err)
		} else {
			opts = append(opts, grpc.WithTransportCredentials(auth))
		}
	} else {
		jwtCreds.RequireTLS = false
		opts = append(opts, grpc.WithInsecure())
	}
	opts = append(opts, grpc.WithPerRPCCredentials(&jwtCreds))

	if connect2api {
		apiUrl = os.Getenv("OTSIMO_SERVICES_API_URL")
		if apiUrl == "" {
			panic("OTSIMO_SERVICES_API_URL must set")
		}
		apiConn, _ := grpc.Dial(apiUrl, opts...)
		apiClient = apipb.NewApiServiceClient(apiConn)
		/*
			if err != nil {
				logrus.Println("                   cannot dial                    ")
				logrus.Println(err)
			} else {
				logrus.Println("                   dialed                         ")
			}

			if apiClient == nil {
				logrus.Println("                   apiClient error               ")
			} else {
				logrus.Println("                   apiClient                  ")
			}
		*/
	}
	//------------------------------------------------------------------------------------------------------------------
	if connect2registry {
		registryUrl = os.Getenv("OTSIMOCTL_REGISTRY")
		if registryUrl == "" {
			panic("OTSIMOCTL_REGISTRY must set")
		}

		regConn, _ := grpc.Dial(registryUrl, opts...)
		registryClient = apipb.NewRegistryServiceClient(regConn)
		/*
			if err != nil {
				logrus.Println("                   cannot dial                    ")
				logrus.Println(err)
			} else {
				logrus.Println("                   dialed                         ")
			}

			if registryClient == nil {
				logrus.Println("                   catalogClt error               ")
			} else {
				logrus.Println("                   catalogClient                  ")
			}
		*/
	}
	//------------------------------------------------------------------------------------------------------------------
	if connect2catalog {
		catalogUrl = os.Getenv("OTSIMOCTL_CATALOG")
		if catalogUrl == "" {
			panic("OTSIMOCTL_CATALOG must set")
		}
		catConn, _ := grpc.Dial(catalogUrl, opts...)
		catalogClient = apipb.NewCatalogServiceClient(catConn)
		/*
			if err != nil {
				logrus.Println("                   cannot dial                    ")
				logrus.Println(err)
			} else {
				logrus.Println("                   dialed                         ")
			}

			if catalogClient == nil {
				logrus.Println("                   catalogClt error               ")
			} else {
				logrus.Println("                   catalogClient                  ")
			}
		*/
	}

}