...

Source file src/github.com/ory/fosite/handler/oauth2/flow_resource_owner.go

Documentation: github.com/ory/fosite/handler/oauth2

     1  /*
     2   * Copyright © 2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   *
    16   * @author		Aeneas Rekkas <aeneas+oss@aeneas.io>
    17   * @copyright 	2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
    18   * @license 	Apache-2.0
    19   *
    20   */
    21  
    22  package oauth2
    23  
    24  import (
    25  	"context"
    26  	"time"
    27  
    28  	"github.com/ory/x/errorsx"
    29  
    30  	"github.com/pkg/errors"
    31  
    32  	"github.com/ory/fosite"
    33  )
    34  
    35  // Deprecated: This handler is deprecated as a means to communicate that the ROPC grant type is widely discouraged and
    36  // is at the time of this writing going to be omitted in the OAuth 2.1 spec. For more information on why this grant type
    37  // is discouraged see: https://www.scottbrady91.com/oauth/why-the-resource-owner-password-credentials-grant-type-is-not-authentication-nor-suitable-for-modern-applications
    38  type ResourceOwnerPasswordCredentialsGrantHandler struct {
    39  	// ResourceOwnerPasswordCredentialsGrantStorage is used to persist session data across requests.
    40  	ResourceOwnerPasswordCredentialsGrantStorage ResourceOwnerPasswordCredentialsGrantStorage
    41  
    42  	RefreshTokenStrategy     RefreshTokenStrategy
    43  	ScopeStrategy            fosite.ScopeStrategy
    44  	AudienceMatchingStrategy fosite.AudienceMatchingStrategy
    45  	RefreshTokenScopes       []string
    46  
    47  	*HandleHelper
    48  }
    49  
    50  // HandleTokenEndpointRequest implements https://tools.ietf.org/html/rfc6749#section-4.3.2
    51  func (c *ResourceOwnerPasswordCredentialsGrantHandler) HandleTokenEndpointRequest(ctx context.Context, request fosite.AccessRequester) error {
    52  	if !c.CanHandleTokenEndpointRequest(request) {
    53  		return errorsx.WithStack(fosite.ErrUnknownRequest)
    54  	}
    55  
    56  	if !request.GetClient().GetGrantTypes().Has("password") {
    57  		return errorsx.WithStack(fosite.ErrUnauthorizedClient.WithHint("The client is not allowed to use authorization grant 'password'."))
    58  	}
    59  
    60  	client := request.GetClient()
    61  	for _, scope := range request.GetRequestedScopes() {
    62  		if !c.ScopeStrategy(client.GetScopes(), scope) {
    63  			return errorsx.WithStack(fosite.ErrInvalidScope.WithHintf("The OAuth 2.0 Client is not allowed to request scope '%s'.", scope))
    64  		}
    65  	}
    66  
    67  	if err := c.AudienceMatchingStrategy(client.GetAudience(), request.GetRequestedAudience()); err != nil {
    68  		return err
    69  	}
    70  
    71  	username := request.GetRequestForm().Get("username")
    72  	password := request.GetRequestForm().Get("password")
    73  	if username == "" || password == "" {
    74  		return errorsx.WithStack(fosite.ErrInvalidRequest.WithHint("Username or password are missing from the POST body."))
    75  	} else if err := c.ResourceOwnerPasswordCredentialsGrantStorage.Authenticate(ctx, username, password); errors.Is(err, fosite.ErrNotFound) {
    76  		return errorsx.WithStack(fosite.ErrInvalidGrant.WithHint("Unable to authenticate the provided username and password credentials.").WithWrap(err).WithDebug(err.Error()))
    77  	} else if err != nil {
    78  		return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
    79  	}
    80  
    81  	// Credentials must not be passed around, potentially leaking to the database!
    82  	delete(request.GetRequestForm(), "password")
    83  
    84  	request.GetSession().SetExpiresAt(fosite.AccessToken, time.Now().UTC().Add(c.AccessTokenLifespan).Round(time.Second))
    85  	if c.RefreshTokenLifespan > -1 {
    86  		request.GetSession().SetExpiresAt(fosite.RefreshToken, time.Now().UTC().Add(c.RefreshTokenLifespan).Round(time.Second))
    87  	}
    88  
    89  	return nil
    90  }
    91  
    92  // PopulateTokenEndpointResponse implements https://tools.ietf.org/html/rfc6749#section-4.3.3
    93  func (c *ResourceOwnerPasswordCredentialsGrantHandler) PopulateTokenEndpointResponse(ctx context.Context, requester fosite.AccessRequester, responder fosite.AccessResponder) error {
    94  	if !c.CanHandleTokenEndpointRequest(requester) {
    95  		return errorsx.WithStack(fosite.ErrUnknownRequest)
    96  	}
    97  
    98  	var refresh, refreshSignature string
    99  	if len(c.RefreshTokenScopes) == 0 || requester.GetGrantedScopes().HasOneOf(c.RefreshTokenScopes...) {
   100  		var err error
   101  		refresh, refreshSignature, err = c.RefreshTokenStrategy.GenerateRefreshToken(ctx, requester)
   102  		if err != nil {
   103  			return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
   104  		} else if err := c.ResourceOwnerPasswordCredentialsGrantStorage.CreateRefreshTokenSession(ctx, refreshSignature, requester.Sanitize([]string{})); err != nil {
   105  			return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
   106  		}
   107  	}
   108  
   109  	if err := c.IssueAccessToken(ctx, requester, responder); err != nil {
   110  		return err
   111  	}
   112  
   113  	if refresh != "" {
   114  		responder.SetExtra("refresh_token", refresh)
   115  	}
   116  
   117  	return nil
   118  }
   119  
   120  func (c *ResourceOwnerPasswordCredentialsGrantHandler) CanSkipClientAuth(requester fosite.AccessRequester) bool {
   121  	return false
   122  }
   123  
   124  func (c *ResourceOwnerPasswordCredentialsGrantHandler) CanHandleTokenEndpointRequest(requester fosite.AccessRequester) bool {
   125  	// grant_type REQUIRED.
   126  	// Value MUST be set to "password".
   127  	return requester.GetGrantTypes().ExactOne("password")
   128  }
   129  

View as plain text