func handleChangeEmailFunc(o *OtsimoAccounts) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { r.ParseForm() oldEmail := r.PostFormValue("old_email") newEmail := r.PostFormValue("new_email") if oldEmail == "" || newEmail == "" || oldEmail == newEmail { log.Info("update.go: invalid body") writeError(w, http.StatusBadRequest, "invalid body") return } resp, err := o.Dex.ChangeEmail(context.Background(), &pb.ChangeEmailRequest{ OldEmail: oldEmail, NewEmail: newEmail, }) log.Infof("update.go: change email result is %q %v", resp, err) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } id := r.Header.Get("sub") if bson.IsObjectIdHex(id) { o.Api.UpdateProfile(context.Background(), &apipb.Profile{Id: bson.ObjectIdHex(id), Email: newEmail}) } writeResponseWithBody(w, http.StatusOK, resp) } else { writeError(w, http.StatusNotFound, "Not Found") } } }
func (s *grpcServer) authToken(jwt jose.JWT) (string, *oidc.ClientMetadata, error) { ciRepo := s.server.ClientIdentityRepo keys, err := s.server.KeyManager.PublicKeys() if err != nil { log.Errorf("grpc.go: Failed to get keys: %v", err) return "", nil, errors.New("errorAccessDenied") } if len(keys) == 0 { log.Error("grpc.go: No keys available for verification client") return "", nil, errors.New("errorAccessDenied") } ok, err := oidc.VerifySignature(jwt, keys) if err != nil { log.Errorf("grpc.go: Failed to verify signature: %v", err) return "", nil, err } if !ok { log.Info("grpc.go: token signature is not verified") return "", nil, errors.New("invalid token") } clientID, err := oidc.VerifyClientClaims(jwt, s.server.IssuerURL.String()) if err != nil { log.Errorf("grpc.go: Failed to verify JWT claims: %v", err) return "", nil, errors.New("failed to verify jwt claims token") } md, err := ciRepo.Metadata(clientID) if md == nil || err != nil { log.Errorf("grpc.go: Failed to find clientID: %s, error=%v", clientID, err) return "", nil, err } //client must be admin in order to use login and register grpc apis. ok, err = ciRepo.IsDexAdmin(clientID) if err != nil { return "", nil, err } if !ok { log.Infof("grpc.go: Client [%s] is not admin", clientID) return "", nil, errors.New("errorAccessDenied") } log.Debugf("grpc.go: Authenticated token for client ID %s", clientID) return clientID, md, nil }
func (l *TokenValidator) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { log.Info("validating") rawToken, err := oidc.ExtractBearerToken(r) if err != nil { log.Error("token.go: failed to get jwt from header") writeError(rw, http.StatusUnauthorized, "missing or invalid token") return } jwt, err := jose.ParseJWT(rawToken) if err != nil { log.Error("token.go: failed to parse jwt") writeError(rw, http.StatusUnauthorized, "missing or invalid token") return } err = l.accounts.Oidc.VerifyJWT(jwt) if err != nil { log.Errorf("token.go: Failed to verify signature: %v", err) writeError(rw, http.StatusUnauthorized, "invalid token") } claims, err := jwt.Claims() if err != nil { log.Error("token.go: failed to get claims", err) writeError(rw, http.StatusUnauthorized, "missing or invalid token") return } sub, ok, err := claims.StringClaim("sub") if err != nil { log.Errorf("token.go: failed to parse 'sub' claim: %v", err) writeError(rw, http.StatusUnauthorized, "missing or invalid token") return } if !ok || sub == "" { log.Error("token.go: missing required 'sub' claim") writeError(rw, http.StatusUnauthorized, "missing or invalid token") return } fmt.Println("token.go: verified token for", sub) r.Header.Set("sub", sub) next(rw, r) }
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()) } }
func (c *clientTokenMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { respondError := func() { writeAPIError(w, http.StatusUnauthorized, newAPIError(errorAccessDenied, "missing or invalid token")) } if c.keysFunc == nil { log.Errorf("Misconfigured clientTokenMiddleware, keysFunc is not set") respondError() return } if c.ciRepo == nil { log.Errorf("Misconfigured clientTokenMiddleware, ClientIdentityRepo is not set") respondError() return } rawToken, err := oidc.ExtractBearerToken(r) if err != nil { log.Errorf("Failed to extract token from request: %v", err) respondError() return } jwt, err := jose.ParseJWT(rawToken) if err != nil { log.Errorf("Failed to parse JWT from token: %v", err) respondError() return } keys, err := c.keysFunc() if err != nil { log.Errorf("Failed to get keys: %v", err) writeAPIError(w, http.StatusUnauthorized, newAPIError(errorAccessDenied, "")) respondError() return } else if len(keys) == 0 { log.Error("No keys available for verification in client token middleware") writeAPIError(w, http.StatusUnauthorized, newAPIError(errorAccessDenied, "")) respondError() return } ok, err := oidc.VerifySignature(jwt, keys) if err != nil { log.Errorf("Failed to verify signature: %v", err) respondError() return } else if !ok { log.Info("Invalid token") respondError() return } clientID, err := oidc.VerifyClientClaims(jwt, c.issuerURL) if err != nil { log.Errorf("Failed to verify JWT claims: %v", err) respondError() return } md, err := c.ciRepo.Metadata(clientID) if md == nil || err != nil { log.Errorf("Failed to find clientID: %s, error=%v", clientID, err) respondError() return } log.Infof("Authenticated token for client ID %s", clientID) c.next.ServeHTTP(w, r) }