@@ -391,6 +391,25 @@ public ValueTask HandleAsync(ProcessAuthenticationContext context)
391
391
}
392
392
}
393
393
394
+ // VK ID uses a non-standard "device_id" parameter in authorization responses.
395
+ else if ( context . Registration . ProviderType is ProviderTypes . VkId )
396
+ {
397
+ var identifier = ( string ? ) context . Request [ "device_id" ] ;
398
+ if ( string . IsNullOrEmpty ( identifier ) )
399
+ {
400
+ context . Reject (
401
+ error : Errors . InvalidRequest ,
402
+ description : SR . FormatID2029 ( "device_id" ) ,
403
+ uri : SR . FormatID8000 ( SR . ID2029 ) ) ;
404
+
405
+ return default ;
406
+ }
407
+
408
+ // Store the device identifier as an authentication property
409
+ // so it can be resolved later to make refresh token requests.
410
+ context . Properties [ VkId . Properties . DeviceId ] = identifier ;
411
+ }
412
+
394
413
// Zoho returns the region of the authenticated user as a non-standard "location" parameter
395
414
// that must be used to compute the address of the token and userinfo endpoints.
396
415
else if ( context . Registration . ProviderType is ProviderTypes . Zoho )
@@ -407,7 +426,7 @@ public ValueTask HandleAsync(ProcessAuthenticationContext context)
407
426
}
408
427
409
428
// Ensure the specified location corresponds to well-known region.
410
- if ( location . ToUpperInvariant ( ) is not ( "AU" or "CA" or "EU" or "IN" or "JP" or "SA" or "US" ) )
429
+ if ( location . ToUpperInvariant ( ) is not ( "AU" or "CA" or "EU" or "IN" or "JP" or "SA" or "US" ) )
411
430
{
412
431
context . Reject (
413
432
error : Errors . InvalidRequest ,
@@ -640,11 +659,18 @@ public ValueTask HandleAsync(ProcessAuthenticationContext context)
640
659
context . TokenRequest . UserCode = code ;
641
660
}
642
661
643
- // VK ID requires flowing the non-standard "device_id" parameter
644
- // from authorization responses to token requests.
662
+ // VK ID requires attaching a non-standard "device_id" parameter to all token requests.
663
+ // This parameter is either resolved from the authorization response (for the authorization
664
+ // code or hybrid grants) or manually provided by the application for other grant types.
645
665
else if ( context . Registration . ProviderType is ProviderTypes . VkId )
646
666
{
647
- context . TokenRequest [ "device_id" ] = context . Request ? [ "device_id" ] ;
667
+ if ( ! context . Properties . TryGetValue ( VkId . Properties . DeviceId , out string ? identifier ) ||
668
+ string . IsNullOrEmpty ( identifier ) )
669
+ {
670
+ throw new InvalidOperationException ( SR . GetResourceString ( SR . ID0467 ) ) ;
671
+ }
672
+
673
+ context . TokenRequest [ "device_id" ] = identifier ;
648
674
}
649
675
650
676
return default ;
@@ -1371,8 +1397,6 @@ public ValueTask HandleAsync(ProcessAuthenticationContext context)
1371
1397
ProviderTypes . Shopify => ( string ? ) context . TokenResponse ? [ "associated_user" ] ? [ "email" ] ,
1372
1398
1373
1399
// Yandex returns the email address as a custom "default_email" node:
1374
- // Note: email node is not a part of standard basic scope
1375
- // (https://yandex.ru/dev/id/doc/en/user-information#common)
1376
1400
ProviderTypes . Yandex => ( string ? ) context . UserInfoResponse ? [ "default_email" ] ,
1377
1401
1378
1402
_ => context . MergedPrincipal . GetClaim ( ClaimTypes . Email )
@@ -1387,7 +1411,7 @@ ProviderTypes.Lichess or ProviderTypes.Mastodon or ProviderTypes.Mixclou
1387
1411
ProviderTypes . Trakt or ProviderTypes . WordPress
1388
1412
=> ( string ? ) context . UserInfoResponse ? [ "username" ] ,
1389
1413
1390
- // Basecamp and Harvest don't return a username so one is created using the "first_name" and "last_name" nodes:
1414
+ // These providers don't return a username so one is created using the "first_name" and "last_name" nodes:
1391
1415
ProviderTypes . Basecamp or ProviderTypes . Harvest or ProviderTypes . VkId
1392
1416
when context . UserInfoResponse ? . HasParameter ( "first_name" ) is true &&
1393
1417
context . UserInfoResponse ? . HasParameter ( "last_name" ) is true
@@ -1435,8 +1459,8 @@ when context.TokenResponse?["associated_user"]?["first_name"] is not null &&
1435
1459
=> $ "{ ( string ? ) context . UserInfoResponse ? [ "firstName" ] } { ( string ? ) context . UserInfoResponse ? [ "lastName" ] } ",
1436
1460
1437
1461
// These providers return the username as a custom "display_name" node:
1438
- ProviderTypes . Spotify or ProviderTypes . StackExchange or ProviderTypes . Yandex or
1439
- ProviderTypes . Zoom
1462
+ ProviderTypes . Spotify or ProviderTypes . StackExchange or
1463
+ ProviderTypes . Yandex or ProviderTypes . Zoom
1440
1464
=> ( string ? ) context . UserInfoResponse ? [ "display_name" ] ,
1441
1465
1442
1466
// Strava returns the username as a custom "athlete/username" node in token responses:
0 commit comments