Beispiel #1
0
func Auth(w http.ResponseWriter, r *http.Request, c router.Context) error {
	db, ok := c.Meta["db"].(*sqlx.DB)
	if !ok {
		return errors.New("db not set in context")
	}
	tokenSecret, ok := c.Meta["tokenSecret"].([]byte)
	if !ok {
		return errors.New("token secret not set in context")
	}

	// parse the token param
	token, err := jwt.ParseFromRequest(r, func(token *jwt.Token) (interface{}, error) {
		return tokenSecret, nil
	})
	if err != nil {
		return res.Unauthorized(w, res.ErrorMsg{"invalid_token", err.Error()})
	}

	// check if the token is eligible for current scope
	//	scope := scopeRegex.FindStringSubmatch(r.URL.Path)[1]
	//	scopes := token.Claims["scopes"].(string)
	//
	//	if !contains(strings.Split(scopes, ","), scope) {
	//		return res.Forbidden(w, res.ErrorMsg{"invalid_scope", "token is not valid for this scope"})
	//	}

	// check if the token was revoked from DB
	t := data.Token{}
	err = t.Get(db, int64(token.Claims["jti"].(float64)))
	if err != nil {
		if _, ok := err.(*data.Error); ok {
			return res.Unauthorized(w, res.ErrorMsg{"invalid_token", "token is not valid"})
		}
		return err
	}
	if t.RevokedAt != nil {
		return res.Unauthorized(w, res.ErrorMsg{"invalid_token", "token is not valid"})
	}

	// valid token
	// set the user id to context and pass to next handler
	c.Meta["user_id"] = t.UserID

	return c.Next(w, r, c)
}
Beispiel #2
0
func TestAddHub(t *testing.T) {
	// setup DB
	db := testhelpers.SetupDB(t)
	defer db.Close()

	// setup server
	ts, err := setupServerHub(db, []byte("secret"))
	if err != nil {
		t.Fatal(err)
	}
	defer ts.Close()

	// create a user
	u := &data.User{
		Username: "******",
		Email:    "*****@*****.**",
	}
	if err := u.EncryptPassword("password"); err != nil {
		t.Fatal(err)
	}
	if err = u.Insert(db); err != nil {
		t.Fatal(err)
	}

	// create a token for the user
	tok := data.Token{
		UserID:    u.ID,
		ExpiresIn: (30 * 24 * time.Hour).Nanoseconds(), // 30 days
	}
	if err := tok.Insert(db); err != nil {
		t.Fatal(err)
	}

	// get the encoded JSON Web Token
	jwt, err := tok.EncodeJWT([]byte("secret"))
	if err != nil {
		t.Fatal(err)
	}

	hub := data.Hub{
		Slug:   "1234",
		UserID: u.ID,
	}
	if err := hub.Insert(db); err != nil {
		t.Fatal(err)
	}

	type testCase struct {
		path       string
		statusCode int
		body       string
	}

	// test when valid params are provided
	spath := "?slug=abcd&access_token=" + jwt
	res, err := http.Get(ts.URL + "/api/v0/hub" + spath)
	if err != nil {
		t.Fatal(err)
	}
	if res.StatusCode != http.StatusOK {
		t.Errorf("%s - Expected status code %v, Got %v", spath, http.StatusOK, res.StatusCode)
	}
	b, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		t.Fatal(err)
	}
	h := data.Hub{}
	if err := json.Unmarshal(b, &h); err != nil {
		t.Errorf("%s - Expected response body to be %+v, Got %s", spath, h, b)
	}

	tCases := []testCase{
		// when slug param is missing
		{"?access_token=" + jwt, http.StatusBadRequest, `{"error":"invalid_request","error_description":"slug required"}`},

		// when access_token param is missing
		{"?slug=abcd", http.StatusUnauthorized, `{"error":"invalid_token","error_description":"no token present in request"}`},

		// when trying to add existing hub
		{"?slug=1234&access_token=" + jwt, http.StatusBadRequest, `{"error":"unique_violation","error_description":"hub exists"}`},
	}
	for _, tc := range tCases {
		res, err := http.Get(ts.URL + "/api/v0/hub" + tc.path)
		if err != nil {
			t.Fatal(err)
		}
		if res.StatusCode != tc.statusCode {
			t.Errorf("%s - Expected status code %v, Got %v", tc.path, tc.statusCode, res.StatusCode)
		}
		b, err := ioutil.ReadAll(res.Body)
		res.Body.Close()
		if err != nil {
			t.Fatal(err)
		}

		if body := string(b); body != tc.body {
			t.Errorf("%s - Expected response body to be %v, Got %v", tc.path, tc.body, body)
		}
	}
}
Beispiel #3
0
func TestShowHub(t *testing.T) {
	// setup DB
	db := testhelpers.SetupDB(t)
	defer db.Close()

	// setup server
	ts, err := setupServerHub(db, []byte("secret"))
	if err != nil {
		t.Fatal(err)
	}
	defer ts.Close()

	// create a user
	u := &data.User{
		Username: "******",
		Email:    "*****@*****.**",
	}
	if err := u.EncryptPassword("password"); err != nil {
		t.Fatal(err)
	}
	if err = u.Insert(db); err != nil {
		t.Fatal(err)
	}

	// create a token for the user
	tok := data.Token{
		UserID:    u.ID,
		ExpiresIn: (30 * 24 * time.Hour).Nanoseconds(), // 30 days
	}
	if err := tok.Insert(db); err != nil {
		t.Fatal(err)
	}

	// get the encoded JSON Web Token
	jwt, err := tok.EncodeJWT([]byte("secret"))
	if err != nil {
		t.Fatal(err)
	}

	hub := data.Hub{
		Slug:   "abcd",
		UserID: u.ID,
	}
	if err := hub.Insert(db); err != nil {
		t.Fatal(err)
	}

	type testCase struct {
		path       string
		statusCode int
		body       string
	}

	tCases := []testCase{
		// when valid params are provided
		{"?access_token=" + jwt, http.StatusOK, `{"hub":["abcd"]}`},

		// when access_token param is missing
		{"?" + jwt, http.StatusUnauthorized, `{"error":"invalid_token","error_description":"no token present in request"}`},
	}
	for _, tc := range tCases {
		res, err := http.Post(ts.URL+"/api/v0/hub"+tc.path, "", nil)
		if err != nil {
			t.Fatal(err)
		}
		if res.StatusCode != tc.statusCode {
			t.Errorf("%s - Expected status code %v, Got %v", tc.path, tc.statusCode, res.StatusCode)
		}
		b, err := ioutil.ReadAll(res.Body)
		res.Body.Close()
		if err != nil {
			t.Fatal(err)
		}

		if body := string(b); body != tc.body {
			t.Errorf("%s - Expected response body to be %v, Got %v", tc.path, tc.body, body)
		}
	}
}
Beispiel #4
0
func TestAuthToken(t *testing.T) {
	// setup DB
	db := testhelpers.SetupDB(t)
	defer db.Close()

	// setup server
	ts, err := setupServer(db, []byte("secret"))
	if err != nil {
		t.Fatal(err)
	}
	defer ts.Close()

	// create a user
	u := &data.User{
		Username: "******",
		Email:    "*****@*****.**",
	}
	if err := u.EncryptPassword("password"); err != nil {
		t.Fatal(err)
	}
	if err = u.Insert(db); err != nil {
		t.Fatal(err)
	}

	// create a token for the user
	tok := data.Token{
		UserID:    u.ID,
		ExpiresIn: (30 * 24 * time.Hour).Nanoseconds(), // 30 days
	}
	if err := tok.Insert(db); err != nil {
		t.Fatal(err)
	}

	// get the encoded JSON Web Token
	jwt, err := tok.EncodeJWT([]byte("secret"))
	if err != nil {
		t.Fatal(err)
	}

	type testCase struct {
		path       string
		statusCode int
		body       string
	}

	tCases := []testCase{
		// when access token not provided
		{"hub", http.StatusUnauthorized, `{"error":"invalid_token","error_description":"no token present in request"}`},

		// when access token is invalid
		{"hub?access_token=invalid", http.StatusUnauthorized, `{"error":"invalid_token","error_description":"token contains an invalid number of segments"}`},

		// // when access token is not properly scoped
		// // fixme currently valid scopes are ["user", "hub", "app"]
		// {"admin?access_token=" + jwt, http.statusforbidden, `{"error":"invalid_scope","error_description":"token is not valid for this scope"}`},

		// when a valid token is provided
		{"hub?access_token=" + jwt, http.StatusOK, `{"status":"ok"}`},

		// when access token is revoked
		// TODO
	}
	for _, tc := range tCases {
		res, err := http.Get(ts.URL + path.Join("/api/v0", tc.path))
		if err != nil {
			t.Fatal(err)
		}
		if res.StatusCode != tc.statusCode {
			t.Errorf("%s - Expected status code %v, Got %v", tc.path, tc.statusCode, res.StatusCode)
		}
		b, err := ioutil.ReadAll(res.Body)
		res.Body.Close()
		if err != nil {
			t.Fatal(err)
		}
		if body := string(b); body != tc.body {
			t.Errorf("%s - Expected response body to be %v, Got %v", tc.path, tc.body, body)
		}
	}
}
Beispiel #5
0
func TestUserToken(t *testing.T) {
	// setup DB
	db := testhelpers.SetupDB(t)
	defer db.Close()

	// setup server
	ts, err := setupServerUser(db, []byte("secret"))
	if err != nil {
		t.Fatal(err)
	}
	defer ts.Close()

	// create a user
	u := &data.User{
		Username: "******",
		Email:    "*****@*****.**",
	}
	if err := u.EncryptPassword("password"); err != nil {
		t.Fatal(err)
	}
	if err = u.Insert(db); err != nil {
		t.Fatal(err)
	}

	tok := data.Token{
		UserID:    u.ID,
		ExpiresIn: (30 * 24 * time.Hour).Nanoseconds(), // 30 days
	}
	if err := tok.Insert(db); err != nil {
		t.Fatal(err)
	}

	//	// get the encoded JSON Web Token
	//	jwt, err := tok.EncodeJWT([]byte("secret"))
	//	if err != nil {
	//		t.Fatal(err)
	//	}

	type testCase struct {
		path       string
		statusCode int
		body       string
	}

	tCases := []testCase{
		// when valid params are provided
		// FIXME: find out why signature in jwt is different from response
		//		{"?grant_type=password&login=foo&password=password", http.StatusOK, `{"access_token":` + jwt + `","token_type":"bearer","expires_in":"720h0m0s"}`},

		// when grant_type param is invalid/missing
		{"?login=foo&password=password", http.StatusBadRequest, `{"error":"unsupported_grant_type","error_description":"supports only password grant type"}`},

		// when login param is missing
		{"?grant_type=password&password=password", http.StatusBadRequest, `{"error":"invalid_request","error_description":"login required"}`},

		// when password param is missing
		{"?grant_type=password&login=foo", http.StatusBadRequest, `{"error":"invalid_request","error_description":"password required"}`},

		// when password value is incorrect
		{"?grant_type=password&login=foo&password=abcd", http.StatusBadRequest, `{"error":"invalid_grant","error_description":"failed to authenticate user"}`},

		// when login value is incorrect
		{"?grant_type=password&login=bar&password=password", http.StatusBadRequest, `{"error":"invalid_grant","error_description":"user not found"}`},
	}

	for _, tc := range tCases {
		res, err := http.Post(ts.URL+"/oauth/token"+tc.path, "", nil)
		if err != nil {
			t.Fatal(err)
		}
		if res.StatusCode != tc.statusCode {
			t.Errorf("%s - Expected status code %v, Got %v", tc.path, tc.statusCode, res.StatusCode)
		}
		b, err := ioutil.ReadAll(res.Body)
		res.Body.Close()
		if err != nil {
			t.Fatal(err)
		}

		if body := string(b); body != tc.body {
			t.Errorf("%s - Expected response body to be %v, Got %v", tc.path, tc.body, body)
		}
	}
}
Beispiel #6
0
// POST /oauth/token
// Params: grant_type, login, password
// Requires a tokenSecret to be set in context
func UserToken(w http.ResponseWriter, r *http.Request, c router.Context) error {
	db, ok := c.Meta["db"].(*sqlx.DB)
	if !ok {
		return errors.New("db not set in context")
	}
	tokenSecret, ok := c.Meta["tokenSecret"].([]byte)
	if !ok {
		return errors.New("token secret not set in context")
	}

	if r.FormValue("grant_type") != "password" {
		return res.BadRequest(w, res.ErrorMsg{"unsupported_grant_type", "supports only password grant type"})
	}

	login := r.FormValue("login")
	if login == "" {
		return res.BadRequest(w, res.ErrorMsg{"invalid_request", "login required"})
	}

	password := r.FormValue("password")
	if password == "" {
		return res.BadRequest(w, res.ErrorMsg{"invalid_request", "password required"})
	}

	u := data.User{}
	if err := u.GetByLogin(db, login); err != nil {
		if e, ok := err.(*data.Error); ok {
			return res.BadRequest(w, res.ErrorMsg{"invalid_grant", e.Desc})
		}
		return err
	}

	if !u.VerifyPassword(password) {
		return res.BadRequest(w, res.ErrorMsg{"invalid_grant", "failed to authenticate user"})
	}

	// Since all is well, generate token and add to database
	t := data.Token{
		UserID:    u.ID,
		ExpiresIn: (30 * 24 * time.Hour).Nanoseconds(), // 30 days
	}
	if err := t.Insert(db); err != nil {
		return err
	}

	// get the encoded JSON Web token
	jwt, err := t.EncodeJWT(tokenSecret)
	if err != nil {
		return err
	}

	// prepare oAuth2 access token payload
	payload := struct {
		AccessToken string `json:"access_token"`
		TokenType   string `json:"token_type"`
		ExpiresIn   string `json:"expires_in"`
	}{
		jwt,
		"bearer",
		time.Duration(t.ExpiresIn).String(),
	}

	return res.OK(w, payload)
}