Menghubungkan ke Azure SQL menggunakan Service Principal di NodeJS, tetapi token ditolak
Saya mengalami masalah dalam mendapatkan aplikasi NodeJS saya untuk menyambungkan ke Azure SQL Database menggunakan Service Principal. Namun, ketika saya mencoba melakukan hal yang sama dengan C # Snippet, itu berfungsi dengan baik. Apa yang saya perhatikan adalah bahwa token yang dikembalikan oleh auth pada kedua bahasa sedikit berbeda, dan jika saya mengambil token yang benar dari C # dan mengkodekannya dengan keras ke NodeJS, Koneksi SQL saya sekarang berhasil.
Saya pertama kali menggunakan ms-rest-azure untuk melakukan otentikasi saya, dan memberikan clientId, tenantId dan clientSecret saya. Ini mengembalikan kembali kredensial yang valid, dari mana saya mengekstrak accessToken.
Kemudian, saya menggunakan membosankan untuk mencoba menyambung ke Azure SQL di * .database.windows.net dan memberikan nilai accessToken dalam konfigurasi.
Saya baru saja Login gagal untuk pengguna '<token-diidentifikasi principal>'
Apa yang saya lakukan salah dalam login ms-rest-azure untuk memberi saya token yang ditolak oleh Azure SQL? Satu hal yang saya lihat adalah bahwa token yang berfungsi memiliki audiens database.windows.net , di mana-seperti yang dari ms-rest-azure adalah management.core.windows.net .
Saya telah terjebak selama beberapa hari, jika ada yang memiliki petunjuk di sini, itu akan luar biasa. Dokumentasi pada ms-rest-azure tampaknya cukup tidak ada dan hanya memberi Anda jalan pintas ke halaman Penjualan Azure.
const msRestAzure = require('ms-rest-azure');
const { reject } = require('async');
let clientSecret = "xxx";
let serverName = "xxx.database.windows.net";
let databaseName = "xxx";
let clientId = "xxx";
let tenantId = "xxx";
azureCredentials = msRestAzure.loginWithServicePrincipalSecret(clientId, clientSecret, tenantId, function(err, credentials) {
if (err) return console.log(err);
credentials.getToken((err, results) => {
if(err) return reject(err);
let accessToken = results.accessToken;
var Connection = require('tedious').Connection;
var Request = require('tedious').Request;
var config = {
server: serverName,
authentication: {
type: 'azure-active-directory-access-token',
options: {
token: accessToken
}
}
,options: {
debug: {
packet: true,
data: true,
payload: true,
token: false,
log: true
},
database: databaseName,
encrypt: true
}
};
var connection = new Connection(config);
connection.connect();
connection.on('connect', function(err) {
if(err) {
console.log(err);
}
executeStatement();
}
);
connection.on('debug', function(text) {
console.log(text);
}
);
function executeStatement() {
request = new Request("select * from Text", function(err, rowCount) {
if (err) {
console.log(err);
} else {
console.log(rowCount + ' rows');
}
connection.close();
});
request.on('row', function(columns) {
columns.forEach(function(column) {
if (column.value === null) {
console.log('NULL');
} else {
console.log(column.value);
}
});
});
request.on('done', function(rowCount, more) {
console.log(rowCount + ' rows returned');
});
connection.execSql(request);
}
});
})
Jawaban
ketika kami menggunakan sertifikat dalam paket ms-rest-azure
untuk mendapatkan token, Secara default, audiens token adalah https://management.core.windows.net/
, itu hanya dapat digunakan untuk memanggil api istirahat Azure. Jika kita ingin menggunakan token Azure AD untuk menghubungkan sql, audiens token haruslah https://database.windows.net/
. Jadi kita harus memperbarui kode yang digunakan untuk mendapatkan token sebagai
msrestAzure.loginWithServicePrincipalSecret(
clientId,
clientSecret,
tenantId,
{
tokenAudience: "https://database.windows.net/",
},
Sebagai contoh
- Buat kepala layanan
az login
az ad sp create-for-rbac -n 'MyApp' --skip-assignment
- Konfigurasikan Database SQL
Sebuah. Gunakan admin Azure Sql AD Anda untuk menyambungkan Azure SQL vai SSMS
b. Tambahkan prinsip layanan ke database yang perlu Anda gunakan
create user [<Azure_AD_principal_name>] from external provider
ALTER ROLE db_owner ADD MEMBER [<Azure_AD_principal_name>]
- kode
var msrestAzure = require("ms-rest-azure");
var { Connection, Request } = require("tedious");
let clientSecret = "xxx";
let serverName = "xxx.database.windows.net";
let databaseName = "xxx";
let clientId = "xxx";
let tenantId = "xxx";
async function getConnect() {
// way for Azure Service Principal
let databaseCredentials = await msrestAzure.loginWithServicePrincipalSecret(
clientId,
clientSecret,
tenantId,
{
tokenAudience: "https://database.windows.net/",
},
);
// getting access token
let databaseAccessToken = await new Promise((resolve, reject) => {
databaseCredentials.getToken((err, results) => {
if (err) return reject(err);
resolve(results.accessToken);
});
});
var config = {
server: serverName,
authentication: {
type: "azure-active-directory-access-token",
options: {
token: databaseAccessToken,
},
},
options: {
debug: {
packet: true,
data: true,
payload: true,
token: false,
log: true,
},
database: databaseName,
encrypt: true,
},
};
var connection = new Connection(config);
connection.connect();
connection.on("connect", function (err) {
if (err) {
console.log(err);
}
executeStatement(connection);
});
connection.on("debug", function (text) {
console.log(text);
});
}
function executeStatement(connection) {
request = new Request("select * from CSVTest", function (err, rowCount) {
if (err) {
console.log(err);
} else {
console.log(rowCount + " rows");
}
connection.close();
});
request.on("row", function (columns) {
columns.forEach(function (column) {
if (column.value === null) {
console.log("NULL");
} else {
console.log(column.value);
}
});
});
request.on("done", function (rowCount, more) {
console.log(rowCount + " rows returned");
});
connection.execSql(request);
}
getConnect()
.then(() => {
console.log("run successfully");
})
.catch((err) => {
console.log(err);
});

Untuk lebih jelasnya, silakan lihat di sini