Skip to content

access_token from OAuth login: already redeemed #1749

@penkyhurk

Description

@penkyhurk

Hello everyone!

I've successfully implemented a proof of concept for an application with Authorization Code Flow.

Calling the code below by browser, shows expected informations, BUT when one reloads the "page" a
"IdentityProviderException" with message "invalid_grant" occures - wich is really missleading IMHO.
Digging deeper one finds this "error_description":
"AADSTS54005: OAuth2 Authorization code was already redeemed, please retry with a new valid code ..."

The code below (as a proof of concept) is part of a SaaS application and should handle different taks.
In this sceanario above exception is thrown from second try to use the script.
As you can see, the "access_token" is not used directly but it seems the SDK uses it internally.

Is there a clean implementation to avoid such exceptions?

I have already a "brute force solution" by redirecting all new calls to the script
to 'https://login.microsoftonline.com/'.TENANT_ID.'/oauth2/v2.0/authorize'
but this realy, realy ugly and far from a clean solution I think.

What is wrong with this code or is this a "misbehavior" of SDK?

   const TENANT_ID        = '...';
   const CLIENT_ID        = '...';
   const SECRET           = '...';
   const REDIRECT_TO_URI  = 'https://example.com/GraphApplicationTest.php';  // placeholder!!

   const MS_AUTHORIZE_URI = 'https://login.microsoftonline.com/'.TENANT_ID.'/oauth2/v2.0/authorize';
   const MS_TOKEN_URI     = 'https://login.microsoftonline.com/'.TENANT_ID.'/oauth2/v2.0/token';
   const MS_RESSOURCE_URI = 'https://login.microsoftonline.com/'.TENANT_ID;


   $basicScopes    = [ 'https://graph.microsoft.com/.default' ];
   $extendedScopes = [
      'openid',
      'offline_access',
      'User.Read',
      'User.Read.All',
      'User.ReadBasic.All',
       ];

   $oauthGenProvider  = NULL;
   $authCode     = "";
   $accessToken  = NULL;
   $nextToken    = NULL;
   $tokenContext = NULL;


   $oauthGenProvider = new GenericProvider([
   		'clientId'                => CLIENT_ID,
   		'clientSecret'            => SECRET,
   		'redirectUri'             => REDIRECT_TO_URI,
   		'urlAuthorize'            => MS_AUTHORIZE_URI,
   		'urlAccessToken'          => MS_TOKEN_URI,
   		'urlResourceOwnerDetails' => MS_RESSOURCE_URI,
   		'scopes'                  => $basicScopes,
   	],
	);


   // check errors from eventually previous request
   if ( isset( $_GET['error'] ) )
   {
   	echo "<pre>";
   	echo "exiting with error!\n\n";
      echo "error: ".$_GET['error']."\n";
      echo "description:\n       ".$_GET['error_description']."\n";
      exit;
   }

   // if not authenticated goto "login"
	if (!isset($_GET['code']))
	{
		$authorizationUrl = $oauthGenProvider->getAuthorizationUrl();
		header('Location: ' . $authorizationUrl);
		exit;
	}





 	echo "<pre>";
	echo "SDK version is ".GraphConstants::SDK_VERSION."\n";


	$authCode = $_GET['code'];

   // first step to prepare for "GraphServiceClient": create a context of type "AuthorizationCodeContext"
   // Microsoft\Kiota\Authentication\Oauth\AuthorizationCodeContext
   // https://github.com/microsoft/kiota-authentication-phpleague-php/blob/main/src/Oauth/AuthorizationCodeContext.php

   $tokenContext = new AuthorizationCodeContext(
      		TENANT_ID,
      		CLIENT_ID,
      		SECRET,
      		$authCode,
            REDIRECT_TO_URI,
      	);

   $graphClient = new GraphServiceClient($tokenContext, $extendedScopes);

   try
   {

      $users = $graphClient->users()->get()->wait();

      $userList = $users->getValue();  // returns an array!

      // $userList[x] <==> Microsoft\Graph\Generated\Models\User
      //                   https://github.com/microsoftgraph/msgraph-sdk-php/blob/main/src/Generated/Models/User.php

      echo "   user[0] display name:   ".$userList[0]->getDisplayName()."\n";

   }
   catch ( Exception $e )
   {
      if ( $e instanceof \League\OAuth2\Client\Provider\Exception\IdentityProviderException )
      {
         echo "   IdentityProviderException\n";
         echo $e->getResponseBody()["error_description"];
         // AADSTS54005: OAuth2 Authorization code was already redeemed, please retry with a new valid code or use an existing refresh token.
      }
      else
      {
         echo "   Exception";
      }
      echo $e->getMessage()."\n";
   }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions