@ram@Emmad_Altaf I used another oauth request library then it worked. normal guzzlehttp didnt work somehow dunno why. but "league/oauth2-client worked fine
$provider = new GenericProvider([
‘clientId’ => env(‘NYLAS_V3_CLIENT_ID’), // The client ID assigned to you by the provider
‘clientSecret’ => env(‘NYLAS_V3_API_KEY’), // The client password assigned to you by the provider
‘redirectUri’ => base_url(env(‘NYLAS_V3_CALLBACK’)),
‘urlAuthorize’ => self::NYLAS_ENDPOINT.‘/connect/auth/’,
‘urlAccessToken’ =>self::NYLAS_ENDPOINT.‘/connect/token/’,
‘urlResourceOwnerDetails’ =>self::NYLAS_ENDPOINT.‘/connect/token/’,
‘prompt’ => ‘select_provider’,
‘login_hint’ => session(‘nylas_email’)
$provider = new GenericProvider([
‘clientId’ => env(‘NYLAS_V3_CLIENT_ID’), // The client ID assigned to you by the provider
‘clientSecret’ => env(‘NYLAS_V3_API_KEY’), // The client password assigned to you by the provider
‘redirectUri’ => base_url(env(‘NYLAS_V3_CALLBACK’)),
‘urlAuthorize’ => self::NYLAS_ENDPOINT.‘/connect/auth/’,
‘urlAccessToken’ =>self::NYLAS_ENDPOINT.‘/connect/token/’,
‘urlResourceOwnerDetails’ =>self::NYLAS_ENDPOINT.‘/connect/token/’,
]);
// Restore the PKCE code stored in the session.
$provider->setPkceCode($_SESSION['oauth2pkceCode']);
// Try to get an access token using the authorization code grant.
$accessToken = $provider->getAccessToken('authorization_code', [
'code' => $_GET['code']
]);
$grantId =$accessToken->getValues();
@Emmad_Altaf as a general practice, request to not share any secret keys or values returned. You can mentioned them as API_KEY or CLIENT_ID just to ensure these values are not publicly available.
For now, I edited your post to remove sensitive values.
Hi @ram and @efeengin ,
Any help about this issue please ?
I am sharing my code to generate code verifier and code challenge, check its ok or there is any mistake in my code:
public async Task EmailandCalendarLogin(NylasAccountsDTO NylasAccount)
{
var response = new Response();
try
{
//var baseUrl = await GetNylasAuthUrl();
var baseUrl = "https://api.eu.nylas.com/";
var callbackUrl = string.Empty;
var configurations = new List<AppConfiguration>();
configurations = await _uow.GetAppConfiguration("Client_Id,IndividualAccount_CallbackUrl,MasterAccount_CallbackUrl");
var individualAccountCallbackUrl = configurations.GetConfiguration<string>("IndividualAccount_CallbackUrl").ToSafeString();
var masterAccountCallbackUrl = configurations.GetConfiguration<string>("MasterAccount_CallbackUrl").ToSafeString();
callbackUrl = NylasAccount.AccountType == 1 ? masterAccountCallbackUrl : individualAccountCallbackUrl;
var clientId = configurations.GetConfiguration<string>("Client_Id").ToSafeString();
var url = $"{baseUrl}v3/connect/auth";
var response_type = "code";
var access_type = "online";
var login_hint = NylasAccount.EmailAddress;
var redirect_uri = callbackUrl;
var providerData = await DetectProvider(NylasAccount);
var provider = providerData.ResultData.ToSafeString();
// Generate code verifier and challenge
var codeVerifier = GenerateCodeVerifier();
var codeChallenge = GenerateCodeChallenge(codeVerifier);
var codeChallengeMethod = "S256";
string sql = string.Empty;
List<SqlParameter> prams = new List<SqlParameter>
{
new SqlParameter("@UserId", _stateManagmentService.UserId),
new SqlParameter("@AccountType", NylasAccount.AccountType)
};
var nylasConfiguredAccountDetail = (await _uow.ExecuteReaderSingleDS<NylasConfiguredAccountDetail>("JT7_GetNylasConfiguredAccountDetail", prams.ToArray())).FirstOrDefault() ?? new NylasConfiguredAccountDetail();
if (nylasConfiguredAccountDetail.EmailAddress == NylasAccount.EmailAddress && nylasConfiguredAccountDetail.AccessToken != string.Empty)
{
prams = new List<SqlParameter>
{
new SqlParameter("@Signature", NylasAccount.Signature.ToSafeString()),
new SqlParameter("@UserId", _stateManagmentService.UserId),
new SqlParameter("@Id", NylasAccount.Id),
new SqlParameter("@AccountType", NylasAccount.AccountType)
};
await _uow.ExecuteNonQuery<int>("JT7_UpdateNylasSignature", prams.ToArray());
response.ResultData = 1;
}
else
{
if (nylasConfiguredAccountDetail != null)
{
prams = new List<SqlParameter>
{
new SqlParameter("@UserId", _stateManagmentService.UserId),
new SqlParameter("@AccountType", NylasAccount.AccountType)
};
await _uow.ExecuteNonQuery<int>("JT7_UnlinkNylasAccount", prams.ToArray());
}
prams = new List<SqlParameter>
{
new SqlParameter("@UserId",NylasAccount.AccountType == 1 ? 0 : _stateManagmentService.UserId),
new SqlParameter("@Signature", NylasAccount.Signature.ToSafeString()),
new SqlParameter("@EmailAddress", string.Empty),
new SqlParameter("@AccountType", NylasAccount.AccountType == 1 ? NylasAccount.AccountType : 2),
};
var output = await _uow.ExecuteScalar<int>("JT7_NylasClientConfigurations_Insert", prams.ToArray());
var nylasHostUrl = $"{url}?client_id={clientId}&redirect_uri={redirect_uri}&response_type={response_type}&access_type={access_type}&provider={provider}&login_hint={login_hint}&code_challenge={codeChallenge}&code_challenge_method={codeChallengeMethod}";
//var nylasHostUrl = $"{url}?client_id={clientId}&redirect_uri={redirect_uri}&response_type={response_type}&access_type={access_type}&provider={provider}&login_hint={login_hint}";
_loggingService.AddErrorLog("nylasHostUrl: " + nylasHostUrl);
response.ResultData = nylasHostUrl;
}
response.Status = Status.Success;
}
catch (Exception ex)
{
response.Status = Status.Failure;
response.Message = _loggingService.AddErrorLog(ex);
}
return response;
}
private string GenerateCodeVerifier()
{
var bytes = new byte[32];
RandomNumberGenerator.Fill(bytes);
var codeVerifier = Base64UrlEncode(bytes);
_httpContext.Session.SetString("code_verifier", codeVerifier);
return codeVerifier;
}
private string GenerateCodeChallenge(string codeVerifier)
{
using (var sha256 = SHA256.Create())
{
var bytes = Encoding.ASCII.GetBytes(codeVerifier);
var hash = sha256.ComputeHash(bytes);
return Base64UrlEncode(hash);
}
}
private string Base64UrlEncode(byte[] bytes)
{
var base64 = Convert.ToBase64String(bytes)
.Replace('+', '-')
.Replace('/', '_')
.TrimEnd('=');
return base64;
}
Hi @ram ,
If I try without the code verifier parameter in Postman, it works fine. But as I mentioned many times, I am facing an issue with the PKCE flow. You can check my detailed comment above. When I am passing the code verifier in my application and also in Postman, it’s generating the same error: “Code verifier challenge failed.”
I have highlighted this issue many times here and also emailed the support team, stating that it is an API issue and the API is broken. The Nylas team should resolve it quickly as it is breaking the whole flow. However, I do not understand why the Nylas team is not checking and addressing this error after so many days. Kindly do not simply ignore this error. I assure you that it is an API issue when using the code verifier.
For issue details, please read the entire conversation above, where I have explained it in detail.
@Emmad_Altaf - thanks for sharing, for reference, I have used the PKCE flow here: https://pomcal.com/ and it works as expected, so I do not see a clear API issue at the moment. I am trying to figure out if there is a way I can reproduce the error. It’s not clear to me, but I will double check your messages.
However, to clarify, why is PKCE required on the backend, is this a requirement or a library that is implementing PKCE? You mentioned previously that PKCE is not a requirement.
I suggest to follow our documentation no implementing PKCE, and to clarify which step you are not able to complete given the code samples that we have available.
Let me know which specific code sample and step does not work for you and I can try to reproduce. I cannot clearly follow the past messages, sorry, but need reproduction steps to follow (especially the code where it does not work).
As an example, something like the following will be helpful:
Hi @ram ,
Can you please test PKCE flow on postman and share screenshot here ?
it will show error to you then you will understand the issue for which i am asking.
@ram The nylas-scheduler-editor component auth returns a code to my redirect-url. Using the nylas ruby SDK, I attempt to exchange the code for a tocken with
I get the error that there is no code verifier present. How is it that the nylas-scheduler-editer component is giving me a code that expects PKCE flow when it doesn’t even return me the information I need to do so?