...

Source file src/github.com/ory/fosite/handler/oauth2/flow_authorize_code_token.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/ory/fosite/storage"
    31  
    32  	"github.com/pkg/errors"
    33  
    34  	"github.com/ory/fosite"
    35  )
    36  
    37  // HandleTokenEndpointRequest implements
    38  // * https://tools.ietf.org/html/rfc6749#section-4.1.3 (everything)
    39  func (c *AuthorizeExplicitGrantHandler) HandleTokenEndpointRequest(ctx context.Context, request fosite.AccessRequester) error {
    40  	if !c.CanHandleTokenEndpointRequest(request) {
    41  		return errorsx.WithStack(errorsx.WithStack(fosite.ErrUnknownRequest))
    42  	}
    43  
    44  	if !request.GetClient().GetGrantTypes().Has("authorization_code") {
    45  		return errorsx.WithStack(fosite.ErrUnauthorizedClient.WithHint("The OAuth 2.0 Client is not allowed to use authorization grant \"authorization_code\"."))
    46  	}
    47  
    48  	code := request.GetRequestForm().Get("code")
    49  	signature := c.AuthorizeCodeStrategy.AuthorizeCodeSignature(code)
    50  	authorizeRequest, err := c.CoreStorage.GetAuthorizeCodeSession(ctx, signature, request.GetSession())
    51  	if errors.Is(err, fosite.ErrInvalidatedAuthorizeCode) {
    52  		if authorizeRequest == nil {
    53  			return fosite.ErrServerError.
    54  				WithHint("Misconfigured code lead to an error that prohibited the OAuth 2.0 Framework from processing this request.").
    55  				WithDebug("GetAuthorizeCodeSession must return a value for \"fosite.Requester\" when returning \"ErrInvalidatedAuthorizeCode\".")
    56  		}
    57  
    58  		// If an authorize code is used twice, we revoke all refresh and access tokens associated with this request.
    59  		reqID := authorizeRequest.GetID()
    60  		hint := "The authorization code has already been used."
    61  		debug := ""
    62  		if revErr := c.TokenRevocationStorage.RevokeAccessToken(ctx, reqID); revErr != nil {
    63  			hint += " Additionally, an error occurred during processing the access token revocation."
    64  			debug += "Revocation of access_token lead to error " + revErr.Error() + "."
    65  		}
    66  		if revErr := c.TokenRevocationStorage.RevokeRefreshToken(ctx, reqID); revErr != nil {
    67  			hint += " Additionally, an error occurred during processing the refresh token revocation."
    68  			debug += "Revocation of refresh_token lead to error " + revErr.Error() + "."
    69  		}
    70  		return errorsx.WithStack(fosite.ErrInvalidGrant.WithHint(hint).WithDebug(debug))
    71  	} else if err != nil && errors.Is(err, fosite.ErrNotFound) {
    72  		return errorsx.WithStack(fosite.ErrInvalidGrant.WithWrap(err).WithDebug(err.Error()))
    73  	} else if err != nil {
    74  		return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
    75  	}
    76  
    77  	// The authorization server MUST verify that the authorization code is valid
    78  	// This needs to happen after store retrieval for the session to be hydrated properly
    79  	if err := c.AuthorizeCodeStrategy.ValidateAuthorizeCode(ctx, request, code); err != nil {
    80  		return errorsx.WithStack(fosite.ErrInvalidGrant.WithWrap(err).WithDebug(err.Error()))
    81  	}
    82  
    83  	// Override scopes
    84  	request.SetRequestedScopes(authorizeRequest.GetRequestedScopes())
    85  
    86  	// Override audiences
    87  	request.SetRequestedAudience(authorizeRequest.GetRequestedAudience())
    88  
    89  	// The authorization server MUST ensure that the authorization code was issued to the authenticated
    90  	// confidential client, or if the client is public, ensure that the
    91  	// code was issued to "client_id" in the request,
    92  	if authorizeRequest.GetClient().GetID() != request.GetClient().GetID() {
    93  		return errorsx.WithStack(fosite.ErrInvalidGrant.WithHint("The OAuth 2.0 Client ID from this request does not match the one from the authorize request."))
    94  	}
    95  
    96  	// ensure that the "redirect_uri" parameter is present if the
    97  	// "redirect_uri" parameter was included in the initial authorization
    98  	// request as described in Section 4.1.1, and if included ensure that
    99  	// their values are identical.
   100  	forcedRedirectURI := authorizeRequest.GetRequestForm().Get("redirect_uri")
   101  	if forcedRedirectURI != "" && forcedRedirectURI != request.GetRequestForm().Get("redirect_uri") {
   102  		return errorsx.WithStack(fosite.ErrInvalidGrant.WithHint("The \"redirect_uri\" from this request does not match the one from the authorize request."))
   103  	}
   104  
   105  	// Checking of POST client_id skipped, because:
   106  	// If the client type is confidential or the client was issued client
   107  	// credentials (or assigned other authentication requirements), the
   108  	// client MUST authenticate with the authorization server as described
   109  	// in Section 3.2.1.
   110  	request.SetSession(authorizeRequest.GetSession())
   111  	request.SetID(authorizeRequest.GetID())
   112  
   113  	request.GetSession().SetExpiresAt(fosite.AccessToken, time.Now().UTC().Add(c.AccessTokenLifespan).Round(time.Second))
   114  	if c.RefreshTokenLifespan > -1 {
   115  		request.GetSession().SetExpiresAt(fosite.RefreshToken, time.Now().UTC().Add(c.RefreshTokenLifespan).Round(time.Second))
   116  	}
   117  
   118  	return nil
   119  }
   120  
   121  func canIssueRefreshToken(c *AuthorizeExplicitGrantHandler, request fosite.Requester) bool {
   122  	// Require one of the refresh token scopes, if set.
   123  	if len(c.RefreshTokenScopes) > 0 && !request.GetGrantedScopes().HasOneOf(c.RefreshTokenScopes...) {
   124  		return false
   125  	}
   126  	// Do not issue a refresh token to clients that cannot use the refresh token grant type.
   127  	if !request.GetClient().GetGrantTypes().Has("refresh_token") {
   128  		return false
   129  	}
   130  	return true
   131  }
   132  
   133  func (c *AuthorizeExplicitGrantHandler) PopulateTokenEndpointResponse(ctx context.Context, requester fosite.AccessRequester, responder fosite.AccessResponder) (err error) {
   134  	if !c.CanHandleTokenEndpointRequest(requester) {
   135  		return errorsx.WithStack(fosite.ErrUnknownRequest)
   136  	}
   137  
   138  	code := requester.GetRequestForm().Get("code")
   139  	signature := c.AuthorizeCodeStrategy.AuthorizeCodeSignature(code)
   140  	authorizeRequest, err := c.CoreStorage.GetAuthorizeCodeSession(ctx, signature, requester.GetSession())
   141  	if err != nil {
   142  		return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
   143  	} else if err := c.AuthorizeCodeStrategy.ValidateAuthorizeCode(ctx, requester, code); err != nil {
   144  		// This needs to happen after store retrieval for the session to be hydrated properly
   145  		return errorsx.WithStack(fosite.ErrInvalidRequest.WithWrap(err).WithDebug(err.Error()))
   146  	}
   147  
   148  	for _, scope := range authorizeRequest.GetGrantedScopes() {
   149  		requester.GrantScope(scope)
   150  	}
   151  
   152  	for _, audience := range authorizeRequest.GetGrantedAudience() {
   153  		requester.GrantAudience(audience)
   154  	}
   155  
   156  	access, accessSignature, err := c.AccessTokenStrategy.GenerateAccessToken(ctx, requester)
   157  	if err != nil {
   158  		return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
   159  	}
   160  
   161  	var refresh, refreshSignature string
   162  	if canIssueRefreshToken(c, authorizeRequest) {
   163  		refresh, refreshSignature, err = c.RefreshTokenStrategy.GenerateRefreshToken(ctx, requester)
   164  		if err != nil {
   165  			return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
   166  		}
   167  	}
   168  
   169  	ctx, err = storage.MaybeBeginTx(ctx, c.CoreStorage)
   170  	if err != nil {
   171  		return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
   172  	}
   173  	defer func() {
   174  		if err != nil {
   175  			if rollBackTxnErr := storage.MaybeRollbackTx(ctx, c.CoreStorage); rollBackTxnErr != nil {
   176  				err = errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebugf("error: %s; rollback error: %s", err, rollBackTxnErr))
   177  			}
   178  		}
   179  	}()
   180  
   181  	if err = c.CoreStorage.InvalidateAuthorizeCodeSession(ctx, signature); err != nil {
   182  		return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
   183  	} else if err = c.CoreStorage.CreateAccessTokenSession(ctx, accessSignature, requester.Sanitize([]string{})); err != nil {
   184  		return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
   185  	} else if refreshSignature != "" {
   186  		if err = c.CoreStorage.CreateRefreshTokenSession(ctx, refreshSignature, requester.Sanitize([]string{})); err != nil {
   187  			return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
   188  		}
   189  	}
   190  
   191  	responder.SetAccessToken(access)
   192  	responder.SetTokenType("bearer")
   193  	responder.SetExpiresIn(getExpiresIn(requester, fosite.AccessToken, c.AccessTokenLifespan, time.Now().UTC()))
   194  	responder.SetScopes(requester.GetGrantedScopes())
   195  	if refresh != "" {
   196  		responder.SetExtra("refresh_token", refresh)
   197  	}
   198  
   199  	if err = storage.MaybeCommitTx(ctx, c.CoreStorage); err != nil {
   200  		return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
   201  	}
   202  
   203  	return nil
   204  }
   205  
   206  func (c *AuthorizeExplicitGrantHandler) CanSkipClientAuth(requester fosite.AccessRequester) bool {
   207  	return false
   208  }
   209  
   210  func (c *AuthorizeExplicitGrantHandler) CanHandleTokenEndpointRequest(requester fosite.AccessRequester) bool {
   211  	// grant_type REQUIRED.
   212  	// Value MUST be set to "authorization_code"
   213  	return requester.GetGrantTypes().ExactOne("authorization_code")
   214  }
   215  

View as plain text