Bonjour à tous,

J'essaie de me connecter à un instance managée de SQL Azure, ce que je parviens à faire en authentification
- SQL server,
- "Active Directory Interactive" (L'AADDS (Azure Active Directory) nous permet de nous authentifier sur le server SQL en MFA).

Nom : user SQL.png
Affichages : 669
Taille : 17,3 Ko


Mon problème : je souhaite me connecter à l'aide d'un token, grace l'attribut de connexion SQL_COPT_SS_ACCESS_TOKEN.

Voici le code que j'utilise pour récupérer mon token:

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
 
procedure TExplorerForm.RESTDemandeToken;
var
  vRESTClient : TRESTClient;
  VRESTREquest: TRESTRequest;
  vUrl,
  vGrantType,
  vTenantID,
  vClientID,
  vClientSecret,
  vRessource ,
  vScope,
  vToken,
  vrefreshToken : String;
begin
  vTenantID     := 'TenantId';
  vClientID     := 'ClientId';
  vClientSecret := 'ClientSecret';
  vGrantType    := 'client_credentials';
  vRessource    := 'https://database.windows.net/';
  vScope        := 'https://management.azure.com/.default';
  vUrl          := 'https://login.microsoftonline.com/' + vTenantID + '/oauth2/v2.0/token';
 
  vRESTClient   := TRESTClient.Create(vUrl);
 
  try
    ConfigureProxy(vRESTClient);
 
    VRESTREquest        := TRESTRequest.Create(vRESTClient);
    VRESTREquest.Method := TRESTRequestMethod.rmPOST;
 
    VRESTREquest.Params.AddItem('grant_type',     vGrantType,    TRESTRequestParameterKind.pkGETorPOST, [], TRESTContentType.ctAPPLICATION_X_WWW_FORM_URLENCODED);
    VRESTREquest.Params.AddItem('client_id',      vClientID,     TRESTRequestParameterKind.pkGETorPOST, [], TRESTContentType.ctAPPLICATION_X_WWW_FORM_URLENCODED);
    VRESTREquest.Params.AddItem('scope',          vScope,        TRESTRequestParameterKind.pkGETorPOST, [], TRESTContentType.ctAPPLICATION_X_WWW_FORM_URLENCODED);
    VRESTREquest.Params.AddItem('client_secret',  vClientSecret, TRESTRequestParameterKind.pkGETorPOST, [], TRESTContentType.ctAPPLICATION_X_WWW_FORM_URLENCODED);
    VRESTREquest.Params.AddItem('ressource',      vRessource,    TRESTRequestParameterKind.pkGETorPOST, [], TRESTContentType.ctAPPLICATION_X_WWW_FORM_URLENCODED);
 
    try
      VRESTREquest.Execute;
    except
      ShowMessage('Problème sur la requête RESTDemandeToken');
    end;
 
    if VRESTREquest.Response.StatusCode = 200 then begin
      if not vRESTRequest.Response.GetSimpleValue('access_token', fToken)
        then ShowMessage('La réponse ne comprend pas de Token');
    end;
 
    fTime := IncSecond(Now, 3599);
    Token_edt.Text := pToken;
 
  finally
    vRESTClient.Free
  end;
end;
J'ai trouvé des exemples en Python, que j'ai essayé d'adapter. (Main_fdc est de type TFDConnection)

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
procedure TExplorerForm.BeforeConnect(Sender: TObject);
var
  vLen,
  vIdx     : integer;
  vLen2    : LongWord;
  vToken,
  vToken_s : string;
  vToken_b : string;
  vODBCAdv : string;
begin
  inherited;
 
  // test "ODBCAdvanced" SQL_COPT_SS_ACCESS_TOKEN (=1256)
  vToken_s := pToken; // Le jeton "database.windows.net"
  vLen := length(vToken_s);
  SetLength(vToken_b,vLen*2);
 
  for var i:=1 to vLen do begin
    vToken_b[(i*2)-1] := vToken_s[i];
    vToken_b[i*2]     := #0;
  end;
 
  vLen2 := Length(vToken_b);
 
  // ODBCAdvanced = attributs de connexion "before" de python ?
  vODBCAdv := 'ODBCAdvanced=1256='
            + vLen2.ToHexString(4)
            + vToken_b;
 
  vIdx := Main_fdc.Params.IndexOfName('ODBCAdvanced');
  if vIdx=-1 then vIdx := Main_fdc.Params.Add('ODBCAdvanced=');
  Main_fdc.Params[vIdx] := vODBCAdv;
 
  // Doc Microsoft stipule qu'il ne faut pas d'Authentication, UID, PWD et Trusted connexion pour SQL_COPT_SS_ACCESS_TOKEN
  vIdx := Main_fdc.Params.IndexOfName('User_Name');
  if vIdx> -1 then Main_fdc.Params.Delete(vIdx);
  vIdx := Main_fdc.Params.IndexOfName('Password');
  if vIdx> -1 then Main_fdc.Params.Delete(vIdx);
 
  vIdx := Main_fdc.Params.IndexOfName('Trusted_Connection');
  if vIdx> -1 then Main_fdc.Params.Delete(vIdx); // n'est pas dans les Params de FiredDac, mais figure bien dans la chaine de connexion finale 
                                                 // voir : FireDac.Phys.ODBCWrapper - TODBCConnction.Connect
 
end;
Mais j'ai un "Login failed for user ''."

Voici un extrait de la chaine de connexion finale

DRIVER=ODBC Driver 17 for SQL Server;Server=xxxxxx;Database=xxxxx;WSID=xxxxx;Trusted_Connection=No;Encrypt=Yes;APP=xxx;1256=0A0Ee#0y#0J#00#0e...
Je me pose plein de questions :
- Le format attendu par SQL server pour le type SQL_IS_POINTER est-il satisfait par la partie "vLen2.ToHexString(4)" (0A0E) ?
- Nous n'avons pas la main sur la chaine de connexion finale (par exemple Trusted_Connection=No est forcée), est-ce le bon moyen, je suis perturbé le "attrs_before" de python

Code : Sélectionner tout - Visualiser dans une fenêtre à part
db = create_engine(SAconnString, connect_args={'attrs_before': {SQL_COPT_SS_ACCESS_TOKEN:tokenstruct}})
-

- que j'ai interprété par un complément "ODBCAdvanced", mais cela peut-il convenir pour envoyer l'attribut SQL_COPT_SS_ACCESS_TOKEN ?

Bref, si quelqu'un s'est déjà frotté au sujet ou a une vue plus expérimentée que la mienne sur le sujet, toute aide serait vraiment bien accueillie

Infos complémentaires :
le lien microsoft pour SQL_COPT_SS_ACCESS_TOKEN
et exemple python




Merci
Renaud