Beispiel #1
0
func LoginHandler(provider string) web.HandlerFunc {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		conf, ok := oauth(ctx, provider)
		if !ok {
			log.Printf("missing oauth provider configuration: %s", provider)
			const code = http.StatusInternalServerError
			http.Error(w, http.StatusText(code), code)
			return
		}

		state := randStr(18)
		url := conf.AuthCodeURL(state, oauth2.AccessTypeOnline)
		http.SetCookie(w, &http.Cookie{
			Name:    stateCookie,
			Path:    "/",
			Value:   state,
			Expires: time.Now().Add(time.Minute * 15),
		})

		nextURL := r.URL.Query().Get("next")
		if nextURL == "" {
			nextURL = "/"
		}
		err := cache.Get(ctx).Put("auth:"+state, &authData{
			Provider: provider,
			Scopes:   conf.Scopes,
			NextURL:  nextURL,
		})
		if err != nil {
			log.Printf("cannot store in cache: %s", err)
			web.StdJSONResp(w, http.StatusInternalServerError)
			return
		}
		web.JSONRedirect(w, url, http.StatusTemporaryRedirect)
	}
}
Beispiel #2
0
func HandleLoginCallback(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	var state string
	if c, err := r.Cookie(stateCookie); err != nil || c.Value == "" {
		log.Printf("invalid oauth state: expected %q, got %q", state, r.FormValue("state"))
		web.JSONRedirect(w, "/", http.StatusTemporaryRedirect)
		return
	} else {
		state = c.Value
	}

	if r.FormValue("state") != state {
		log.Printf("invalid oauth state: expected %q, got %q", state, r.FormValue("state"))
		web.JSONRedirect(w, "/", http.StatusTemporaryRedirect)
		return
	}

	var data authData
	switch err := cache.Get(ctx).Get("auth:"+state, &data); err {
	case nil:
		// all good
	case cache.ErrNotFound:
		web.JSONRedirect(w, "/", http.StatusTemporaryRedirect)
		return
	default:
		log.Printf("cannot get auth data from cache: %s", err)
		web.JSONRedirect(w, "/", http.StatusTemporaryRedirect)
		return
	}

	conf, ok := oauth(ctx, data.Provider)
	if !ok {
		log.Printf("missing oauth provider configuration: %#v", data)
		const code = http.StatusInternalServerError
		http.Error(w, http.StatusText(code), code)
		return
	}

	token, err := conf.Exchange(oauth2.NoContext, r.FormValue("code"))
	if err != nil {
		log.Printf("oauth exchange failed: %s", err)
		web.JSONRedirect(w, "/", http.StatusTemporaryRedirect)
		return
	}
	cli := google.NewClient(conf.Client(oauth2.NoContext, token))
	user, _, err := cli.Users.Get("")
	if err != nil {
		log.Printf("cannot get user: %s", err)
		web.JSONRedirect(w, "/", http.StatusTemporaryRedirect)
		return
	}

	db := pg.DB(ctx)
	tx, err := db.Beginx()
	if err != nil {
		log.Printf("cannot start transaction: %s", err)
		http.Error(w, http.StatusText(http.StatusServiceUnavailable), http.StatusServiceUnavailable)
		return
	}
	defer tx.Rollback()

	provider := strings.SplitN(data.Provider, ":", 2)[0]
	acc, err := AccountByLogin(tx, *user.Login, provider)
	if err != nil {
		if err != pg.ErrNotFound {
			log.Printf("cannot get account %s: %s", *user.Login, err)
			http.Error(w, "cannot authenticate", http.StatusInternalServerError)
			return
		}

		acc, err = CreateAccount(tx, *user.ID, *user.Login, provider)
		if err != nil {
			log.Printf("cannot create account for %v: %s", user, err)
			http.Error(w, "cannot create account", http.StatusInternalServerError)
			return
		}
	}

	if err := authenticate(tx, w, acc.AccountID, token.AccessToken, data.Scopes); err != nil {
		log.Printf("cannot authenticate %#v: %s", acc, err)
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	if err := tx.Commit(); err != nil {
		log.Printf("cannot commit transaction: %s", err)
		http.Error(w, http.StatusText(http.StatusServiceUnavailable), http.StatusServiceUnavailable)
		return
	}

	web.JSONRedirect(w, data.NextURL, http.StatusTemporaryRedirect)
}