// GetOrCreateRefreshToken retrieves an existing refresh token, if expired, // the token gets deleted and new refresh token is created func (s *Service) GetOrCreateRefreshToken(client *models.OauthClient, user *models.OauthUser, expiresIn int, scope string) (*models.OauthRefreshToken, error) { // Try to fetch an existing refresh token first refreshToken := new(models.OauthRefreshToken) query := models.OauthRefreshTokenPreload(s.db).Where("client_id = ?", client.ID) if user != nil && user.ID > 0 { query = query.Where("user_id = ?", user.ID) } else { query = query.Where("user_id IS NULL") } found := !query.First(refreshToken).RecordNotFound() // Check if the token is expired, if found var expired bool if found { expired = time.Now().UTC().After(refreshToken.ExpiresAt) } // If the refresh token has expired, delete it if expired { s.db.Unscoped().Delete(refreshToken) } // Create a new refresh token if it expired or was not found if expired || !found { refreshToken = models.NewOauthRefreshToken(client, user, expiresIn, scope) if err := s.db.Create(refreshToken).Error; err != nil { return nil, err } refreshToken.Client = client refreshToken.User = user } return refreshToken, nil }
func (suite *OauthTestSuite) TestClientCredentialsGrant() { // Prepare a request r, err := http.NewRequest("POST", "http://1.2.3.4/v1/oauth/tokens", nil) assert.NoError(suite.T(), err, "Request setup should not get an error") r.SetBasicAuth("test_client_1", "test_secret") r.PostForm = url.Values{ "grant_type": {"client_credentials"}, "scope": {"read_write"}, } // Serve the request w := httptest.NewRecorder() suite.router.ServeHTTP(w, r) // Fetch data accessToken := new(models.OauthAccessToken) assert.False(suite.T(), models.OauthAccessTokenPreload(suite.db). Last(accessToken).RecordNotFound()) // Check the response expected := &oauth.AccessTokenResponse{ AccessToken: accessToken.Token, ExpiresIn: 3600, TokenType: tokentypes.Bearer, Scope: "read_write", } testutil.TestResponseObject(suite.T(), w, expected, 200) // Client credentials grant does not produce refresh token assert.True(suite.T(), models.OauthRefreshTokenPreload(suite.db). First(new(models.OauthRefreshToken)).RecordNotFound()) }
func (suite *OauthTestSuite) TestPasswordGrant() { // Prepare a request r, err := http.NewRequest("POST", "http://1.2.3.4/v1/oauth/tokens", nil) assert.NoError(suite.T(), err, "Request setup should not get an error") r.SetBasicAuth("test_client_1", "test_secret") r.PostForm = url.Values{ "grant_type": {"password"}, "username": {"test@user"}, "password": {"test_password"}, "scope": {"read_write"}, } // Serve the request w := httptest.NewRecorder() suite.router.ServeHTTP(w, r) // Fetch data accessToken, refreshToken := new(models.OauthAccessToken), new(models.OauthRefreshToken) assert.False(suite.T(), models.OauthAccessTokenPreload(suite.db). Last(accessToken).RecordNotFound()) assert.False(suite.T(), models.OauthRefreshTokenPreload(suite.db). Last(refreshToken).RecordNotFound()) // Check the response expected := &oauth.AccessTokenResponse{ UserID: accessToken.User.MetaUserID, AccessToken: accessToken.Token, ExpiresIn: 3600, TokenType: tokentypes.Bearer, Scope: "read_write", RefreshToken: refreshToken.Token, } testutil.TestResponseObject(suite.T(), w, expected, 200) }
func (suite *OauthTestSuite) TestAuthorizationCodeGrant() { // Insert a test authorization code err := suite.db.Create(&models.OauthAuthorizationCode{ Code: "test_code", ExpiresAt: time.Now().UTC().Add(+10 * time.Second), Client: suite.clients[0], User: suite.users[0], RedirectURI: util.StringOrNull("https://www.example.com"), Scope: "read_write", }).Error assert.NoError(suite.T(), err, "Inserting test data failed") // Prepare a request r, err := http.NewRequest("POST", "http://1.2.3.4/v1/oauth/tokens", nil) assert.NoError(suite.T(), err, "Request setup should not get an error") r.SetBasicAuth("test_client_1", "test_secret") r.PostForm = url.Values{ "grant_type": {"authorization_code"}, "code": {"test_code"}, "redirect_uri": {"https://www.example.com"}, } // Serve the request w := httptest.NewRecorder() suite.router.ServeHTTP(w, r) // Fetch data accessToken, refreshToken := new(models.OauthAccessToken), new(models.OauthRefreshToken) assert.False(suite.T(), models.OauthAccessTokenPreload(suite.db). Last(accessToken).RecordNotFound()) assert.False(suite.T(), models.OauthRefreshTokenPreload(suite.db). Last(refreshToken).RecordNotFound()) // Check the response expected := &oauth.AccessTokenResponse{ UserID: accessToken.User.MetaUserID, AccessToken: accessToken.Token, ExpiresIn: 3600, TokenType: tokentypes.Bearer, Scope: "read_write", RefreshToken: refreshToken.Token, } testutil.TestResponseObject(suite.T(), w, expected, 200) // The authorization code should get deleted after use assert.True(suite.T(), suite.db.Unscoped(). First(new(models.OauthAuthorizationCode)).RecordNotFound()) }
// GetValidRefreshToken returns a valid non expired refresh token func (s *Service) GetValidRefreshToken(token string, client *models.OauthClient) (*models.OauthRefreshToken, error) { // Fetch the refresh token from the database refreshToken := new(models.OauthRefreshToken) notFound := models.OauthRefreshTokenPreload(s.db).Where("client_id = ?", client.ID). Where("token = ?", token).First(refreshToken).RecordNotFound() // Not found if notFound { return nil, ErrRefreshTokenNotFound } // Check the refresh token hasn't expired if time.Now().UTC().After(refreshToken.ExpiresAt) { return nil, ErrRefreshTokenExpired } return refreshToken, nil }
func (suite *OauthTestSuite) TestGetOrCreateRefreshTokenDeletesExpired() { var ( refreshToken *models.OauthRefreshToken err error tokens []*models.OauthRefreshToken ) // Insert an expired client only test refresh token err = suite.db.Create(&models.OauthRefreshToken{ Token: "test_token", ExpiresAt: time.Now().UTC().Add(-10 * time.Second), Client: suite.clients[0], }).Error assert.NoError(suite.T(), err, "Inserting test data failed") // Since the current client only token is expired, // this should delete it and create and return a new one refreshToken, err = suite.service.GetOrCreateRefreshToken( suite.clients[0], // client nil, // user 3600, // expires in "read_write", // scope ) // Error should be nil if assert.Nil(suite.T(), err) { // Fetch all refresh tokens models.OauthRefreshTokenPreload(suite.db.Unscoped()).Order("id").Find(&tokens) // There should be just one token right now assert.Equal(suite.T(), 1, len(tokens)) // Correct refresh token object should be returned assert.NotNil(suite.T(), refreshToken) assert.Equal(suite.T(), tokens[0].Token, refreshToken.Token) assert.NotEqual(suite.T(), "test_token", refreshToken.Token) assert.NotEqual(suite.T(), "test_token", tokens[0].Token) // Client ID should be set assert.True(suite.T(), tokens[0].ClientID.Valid) assert.Equal(suite.T(), int64(suite.clients[0].ID), tokens[0].ClientID.Int64) // User ID should be nil assert.False(suite.T(), tokens[0].UserID.Valid) } // Insert an expired user specific test refresh token err = suite.db.Create(&models.OauthRefreshToken{ Token: "test_token", ExpiresAt: time.Now().UTC().Add(-10 * time.Second), Client: suite.clients[0], User: suite.users[0], }).Error assert.NoError(suite.T(), err, "Inserting test data failed") // Since the current user specific token is expired, // this should delete it and create and return a new one refreshToken, err = suite.service.GetOrCreateRefreshToken( suite.clients[0], // client suite.users[0], // user 3600, // expires in "read_write", // scope ) // Error should be nil if assert.Nil(suite.T(), err) { // Fetch all refresh tokens models.OauthRefreshTokenPreload(suite.db.Unscoped()).Order("id").Find(&tokens) // There should be 2 tokens now assert.Equal(suite.T(), 2, len(tokens)) // Correct refresh token object should be returned assert.NotNil(suite.T(), refreshToken) assert.Equal(suite.T(), tokens[1].Token, refreshToken.Token) assert.NotEqual(suite.T(), "test_token", refreshToken.Token) assert.NotEqual(suite.T(), "test_token", tokens[1].Token) // Client ID should be set assert.True(suite.T(), tokens[1].ClientID.Valid) assert.Equal(suite.T(), int64(suite.clients[0].ID), tokens[1].ClientID.Int64) // User ID should be set assert.True(suite.T(), tokens[1].UserID.Valid) assert.Equal(suite.T(), int64(suite.users[0].ID), tokens[1].UserID.Int64) } }
func (suite *OauthTestSuite) TestGetOrCreateRefreshTokenCreatesNew() { var ( refreshToken *models.OauthRefreshToken err error tokens []*models.OauthRefreshToken ) // Since there is no user specific token, // a new one should be created and returned refreshToken, err = suite.service.GetOrCreateRefreshToken( suite.clients[0], // client suite.users[0], // user 3600, // expires in "read_write", // scope ) // Error should be nil if assert.Nil(suite.T(), err) { // Fetch all refresh tokens models.OauthRefreshTokenPreload(suite.db).Order("id").Find(&tokens) // There should be just one token now assert.Equal(suite.T(), 1, len(tokens)) // Correct refresh token object should be returned assert.NotNil(suite.T(), refreshToken) assert.Equal(suite.T(), tokens[0].Token, refreshToken.Token) // Client ID should be set assert.True(suite.T(), tokens[0].ClientID.Valid) assert.Equal(suite.T(), suite.clients[0].ID, tokens[0].Client.ID) // User ID should be set assert.True(suite.T(), tokens[0].UserID.Valid) assert.Equal(suite.T(), suite.users[0].ID, tokens[0].User.ID) } // Valid user specific token exists, new one should NOT be created refreshToken, err = suite.service.GetOrCreateRefreshToken( suite.clients[0], // client suite.users[0], // user 3600, // expires in "read_write", // scope ) // Error should be nil if assert.Nil(suite.T(), err) { // Fetch all refresh tokens models.OauthRefreshTokenPreload(suite.db).Order("id").Find(&tokens) // There should be just one token now assert.Equal(suite.T(), 1, len(tokens)) // Correct refresh token object should be returned assert.NotNil(suite.T(), refreshToken) assert.Equal(suite.T(), tokens[0].Token, refreshToken.Token) // Client ID should be set assert.True(suite.T(), tokens[0].ClientID.Valid) assert.Equal(suite.T(), suite.clients[0].ID, tokens[0].Client.ID) // User ID should be set assert.True(suite.T(), tokens[0].UserID.Valid) assert.Equal(suite.T(), suite.users[0].ID, tokens[0].User.ID) } // Since there is no client only token, // a new one should be created and returned refreshToken, err = suite.service.GetOrCreateRefreshToken( suite.clients[0], // client nil, // user 3600, // expires in "read_write", // scope ) // Error should be nil if assert.Nil(suite.T(), err) { // Fetch all refresh tokens models.OauthRefreshTokenPreload(suite.db).Order("id").Find(&tokens) // There should be 2 tokens assert.Equal(suite.T(), 2, len(tokens)) // Correct refresh token object should be returned assert.NotNil(suite.T(), refreshToken) assert.Equal(suite.T(), tokens[1].Token, refreshToken.Token) // Client ID should be set assert.True(suite.T(), tokens[1].ClientID.Valid) assert.Equal(suite.T(), suite.clients[0].ID, tokens[1].Client.ID) // User ID should be nil assert.False(suite.T(), tokens[1].UserID.Valid) } // Valid client only token exists, new one should NOT be created refreshToken, err = suite.service.GetOrCreateRefreshToken( suite.clients[0], // client nil, // user 3600, // expires in "read_write", // scope ) // Error should be nil if assert.Nil(suite.T(), err) { // Fetch all refresh tokens models.OauthRefreshTokenPreload(suite.db).Order("id").Find(&tokens) // There should be 2 tokens assert.Equal(suite.T(), 2, len(tokens)) // Correct refresh token object should be returned assert.NotNil(suite.T(), refreshToken) assert.Equal(suite.T(), tokens[1].Token, refreshToken.Token) // Client ID should be set assert.True(suite.T(), tokens[1].ClientID.Valid) assert.Equal(suite.T(), suite.clients[0].ID, tokens[1].Client.ID) // User ID should be nil assert.False(suite.T(), tokens[1].UserID.Valid) } }