@@ -519,3 +519,65 @@ class OpenIDClientStub implements BaseClient {
519519 } ) ;
520520 } ) ;
521521} ) ;
522+
523+ describe ( 'AzureOAuthManager (tenant awareness)' , ( ) => {
524+ function makeAzure ( overrides : Partial < OAuthManagerOptions > = { } ) {
525+ return new AzureOAuthManager ( {
526+ host : 'adb-1234567890123456.1.azuredatabricks.net' ,
527+ flow : OAuthFlow . M2M ,
528+ context : new ClientContextStub ( ) ,
529+ ...overrides ,
530+ } ) ;
531+ }
532+
533+ // Access protected methods for unit inspection.
534+ const call = < T > ( mgr : AzureOAuthManager , name : string , ...args : unknown [ ] ) : T =>
535+ ( mgr as unknown as Record < string , ( ...a : unknown [ ] ) => T > ) [ name ] ( ...args ) ;
536+
537+ describe ( 'getOIDCConfigUrl' , ( ) => {
538+ it ( 'falls back to /organizations/ when azureTenantId is not set (baseline-compatible)' , ( ) => {
539+ const mgr = makeAzure ( ) ;
540+ const url = call < string > ( mgr , 'getOIDCConfigUrl' ) ;
541+ expect ( url ) . to . equal ( 'https://login.microsoftonline.com/organizations/v2.0/.well-known/openid-configuration' ) ;
542+ } ) ;
543+
544+ it ( 'uses the caller-supplied tenant in the discovery URL when provided' , ( ) => {
545+ const tenant = '9f37a392-f0ae-4280-9796-f1864a10effc' ;
546+ const mgr = makeAzure ( { azureTenantId : tenant } ) ;
547+ const url = call < string > ( mgr , 'getOIDCConfigUrl' ) ;
548+ expect ( url ) . to . equal ( `https://login.microsoftonline.com/${ tenant } /v2.0/.well-known/openid-configuration` ) ;
549+ } ) ;
550+
551+ it ( 'falls back to /organizations/ when azureTenantId is empty or whitespace' , ( ) => {
552+ for ( const tenant of [ '' , ' ' ] ) {
553+ const mgr = makeAzure ( { azureTenantId : tenant } ) ;
554+ const url = call < string > ( mgr , 'getOIDCConfigUrl' ) ;
555+ expect ( url ) . to . equal ( 'https://login.microsoftonline.com/organizations/v2.0/.well-known/openid-configuration' ) ;
556+ }
557+ } ) ;
558+ } ) ;
559+
560+ describe ( 'getScopes — resource ID is always the Azure Login App, never a tenant GUID' , ( ) => {
561+ it ( 'M2M scope uses the Databricks Azure Login App resource ID even when azureTenantId is set' , ( ) => {
562+ const tenant = '9f37a392-f0ae-4280-9796-f1864a10effc' ;
563+ const mgr = makeAzure ( { azureTenantId : tenant , flow : OAuthFlow . M2M } ) ;
564+ const scopes = call < string [ ] > ( mgr , 'getScopes' , [ ] ) ;
565+ expect ( scopes ) . to . include ( `${ AzureOAuthManager . datatricksAzureApp } /.default` ) ;
566+ expect ( scopes ) . to . not . include ( `${ tenant } /.default` ) ;
567+ } ) ;
568+
569+ it ( 'U2M scope uses the Databricks Azure Login App resource ID even when azureTenantId is set' , ( ) => {
570+ const tenant = '9f37a392-f0ae-4280-9796-f1864a10effc' ;
571+ const mgr = makeAzure ( { azureTenantId : tenant , flow : OAuthFlow . U2M } ) ;
572+ const scopes = call < string [ ] > ( mgr , 'getScopes' , [ ] ) ;
573+ expect ( scopes ) . to . include ( `${ AzureOAuthManager . datatricksAzureApp } /user_impersonation` ) ;
574+ expect ( scopes ) . to . not . include ( `${ tenant } /user_impersonation` ) ;
575+ } ) ;
576+
577+ it ( 'preserves offline_access when requested alongside M2M' , ( ) => {
578+ const mgr = makeAzure ( { flow : OAuthFlow . M2M } ) ;
579+ const scopes = call < string [ ] > ( mgr , 'getScopes' , [ OAuthScope . offlineAccess ] ) ;
580+ expect ( scopes ) . to . include ( OAuthScope . offlineAccess ) ;
581+ } ) ;
582+ } ) ;
583+ } ) ;
0 commit comments