Microsoft Entra Verified ID omvat de REST API van de aanvraagservice. Met deze API kunt u referenties uitgeven en verifiëren. In dit artikel leest u hoe u de REST API van de aanvraagservice kunt gaan gebruiken.
API-toegangstoken
Uw toepassing moet een geldig toegangstoken met de vereiste machtigingen bevatten, zodat deze toegang heeft tot de REST API van de aanvraagservice. Toegangstokens die zijn uitgegeven door het Microsoft Identity Platform bevatten informatie (bereiken) die door de REST API van de aanvraagservice worden gebruikt om de aanroeper te valideren. Een toegangstoken zorgt ervoor dat de aanroeper over de juiste machtigingen beschikt om de bewerking uit te voeren die ze aanvragen.
Als u een toegangstoken wilt ophalen, moet uw app worden geregistreerd bij het Microsoft Identity Platform en worden geautoriseerd door een beheerder voor toegang tot de REST API van de aanvraagservice. Als u de toepassing verifieerbare-credentials-app nog niet hebt geregistreerd, bekijkt u hoe u de app registreert en daarna het toepassingsgeheim genereert.
Een toegangstoken ophalen
Gebruik de OAuth 2.0-cliëntreferenties flow om het toegangstoken te verkrijgen met behulp van het Microsoft Identity Platform. Gebruik hiervoor een vertrouwde bibliotheek. In deze zelfstudie gebruiken we de Microsoft Authentication Library (MSAL-). MSAL vereenvoudigt het toevoegen van verificatie en autorisatie aan een app die een beveiligde web-API kan aanroepen.
POST /{tenant}/oauth2/v2.0/token HTTP/1.1 //Line breaks for clarity
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&scope=3db474b9-6a0c-4840-96ac-1fceb342124f/.default
&client_secret=sampleCredentia1s
&grant_type=client_credentials
// Initialize MSAL library by using the following code
ConfidentialClientApplicationBuilder.Create(AppSettings.ClientId)
.WithClientSecret(AppSettings.ClientSecret)
.WithAuthority(new Uri(AppSettings.Authority))
.Build();
// Acquire an access token
result = await app.AcquireTokenForClient(AppSettings.Scopes)
.ExecuteAsync();
// Initialize MSAL library by using the following code
const msalConfig = {
auth: {
clientId: config.azClientId,
authority: `https://login.microsoftonline.com/${config.azTenantId}`,
clientSecret: config.azClientSecret,
},
system: {
loggerOptions: {
loggerCallback(loglevel, message, containsPii) {
console.log(message);
},
piiLoggingEnabled: false,
logLevel: msal.LogLevel.Verbose,
}
}
};
const cca = new msal.ConfidentialClientApplication(msalConfig);
const msalClientCredentialRequest = {
scopes: ["3db474b9-6a0c-4840-96ac-1fceb342124f/.default"],
skipCache: false,
};
module.exports.msalCca = cca;
module.exports.msalClientCredentialRequest = msalClientCredentialRequest;
// Acquire an access token
const result = await mainApp.msalCca.acquireTokenByClientCredential(mainApp.msalClientCredentialRequest);
if ( result ) {
accessToken = result.accessToken;
}
# Initialize MSAL library by using the following code
msalCca = msal.ConfidentialClientApplication( config["azClientId"],
authority="https://login.microsoftonline.com/" + config["azTenantId"],
client_credential=config["azClientSecret"],
)
# Acquire an access token
accessToken = ""
result = msalCca.acquire_token_for_client( scopes="3db474b9-6a0c-4840-96ac-1fceb342124f/.default" )
if "access_token" in result:
accessToken = result['access_token']
// Initialize MSAL library by using the following code
ConfidentialClientApplication app = ConfidentialClientApplication.builder(
clientId,
ClientCredentialFactory.createFromSecret(clientSecret))
.authority(authority)
.build();
// Acquire an access token
ClientCredentialParameters clientCredentialParam = ClientCredentialParameters.builder(
Collections.singleton(scope))
.build();
CompletableFuture<IAuthenticationResult> future = app.acquireToken(clientCredentialParam);
IAuthenticationResult result = future.get();
return result.accessToken();
Geef in de voorgaande code de volgende parameters op:
Kenmerk |
Conditie |
Beschrijving |
Autoriteit |
Vereist |
De directory tenant waarmee de toepassing wil werken. Bijvoorbeeld: https://login.microsoftonline.com/{your-tenant} . (Vervang your-tenant door uw tenant-id of -naam.) |
Klant-ID |
Vereist |
De toepassings-id die is toegewezen aan uw app. U vindt deze informatie in Azure Portal, waar u uw app hebt geregistreerd. |
Clientgeheim |
Vereist |
Het clientgeheim dat u hebt gegenereerd voor uw app. |
Toepassingsgebieden |
Vereist |
Moet worden ingesteld op 3db474b9-6a0c-4840-96ac-1fceb342124f/.default . Deze instelling produceert een toegangstoken met een rollen claim van VerifiableCredential.Create.All . |
Zie een van de volgende artikelen voor meer informatie over het verkrijgen van een toegangstoken met behulp van de identiteit van een console-app:
U kunt ook toegang krijgen tot een tokenaanvraag met een certificaat in plaats van een clientgeheim.
POST /{tenant}/oauth2/v2.0/token HTTP/1.1 //Line breaks for clarity
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&scope=3db474b9-6a0c-4840-96ac-1fceb342124f/.default
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJhbGciOiJSUzI1NiIsIng1dCI6Imd4OHRHeXN5amNScUtqRlBuZDdSRnd2d1pJMCJ9.eyJ{a lot of characters here}M8U3bSUKKJDEg
&grant_type=client_credentials
// Initialize MSAL library by using the following code
X509Certificate2 certificate = AppSettings.ReadCertificate(AppSettings.CertificateName);
app = ConfidentialClientApplicationBuilder.Create(AppSettings.ClientId)
.WithCertificate(certificate)
.WithAuthority(new Uri(AppSettings.Authority))
.Build();
// Acquire an access token
result = await app.AcquireTokenForClient(AppSettings.Scopes)
.ExecuteAsync();
// Initialize MSAL library by using the following code
const msalConfig = {
auth: {
clientId: config.azClientId,
authority: `https://login.microsoftonline.com/${config.azTenantId}`,
clientCertificate: {
thumbprint: "CERT_THUMBPRINT", // a 40-digit hexadecimal string
privateKey: "CERT_PRIVATE_KEY"
}
},
system: {
loggerOptions: {
loggerCallback(loglevel, message, containsPii) {
console.log(message);
},
piiLoggingEnabled: false,
logLevel: msal.LogLevel.Verbose,
}
}
};
const cca = new msal.ConfidentialClientApplication(msalConfig);
const msalClientCredentialRequest = {
scopes: ["3db474b9-6a0c-4840-96ac-1fceb342124f/.default"],
skipCache: false,
};
module.exports.msalCca = cca;
module.exports.msalClientCredentialRequest = msalClientCredentialRequest;
// Acquire an access token
const result = await mainApp.msalCca.acquireTokenByClientCredential(mainApp.msalClientCredentialRequest);
if ( result ) {
accessToken = result.accessToken;
}
# Initialize MSAL library by using the following code
with open(config["azCertificatePrivateKeyLocation"], "rb") as file:
private_key = file.read()
with open(config["azCertificateLocation"]) as file:
public_certificate = file.read()
cert = load_pem_x509_certificate(data=bytes(public_certificate, 'UTF-8'), backend=default_backend())
thumbprint = (cert.fingerprint(hashes.SHA1()).hex())
msalCca = msal.ConfidentialClientApplication( config["azClientId"],
authority="https://login.microsoftonline.com/" + config["azTenantId"],
client_credential={
"private_key": private_key,
"thumbprint": thumbprint,
"public_certificate": public_certificate
}
)
# Acquire an access token
accessToken = ""
result = msalCca.acquire_token_for_client( scopes="3db474b9-6a0c-4840-96ac-1fceb342124f/.default" )
if "access_token" in result:
accessToken = result['access_token']
// Initialize MSAL library by using the following code
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Files.readAllBytes(Paths.get(certKeyLocation)));
PrivateKey key = KeyFactory.getInstance("RSA").generatePrivate(spec);
java.io.InputStream certStream = (java.io.InputStream)new ByteArrayInputStream(Files.readAllBytes(Paths.get(certLocation)));
X509Certificate cert = (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(certStream);
ConfidentialClientApplication app = ConfidentialClientApplication.builder(
clientId,
ClientCredentialFactory.createFromCertificate(key, cert))
.authority(authority)
.build();
// Acquire an access token
ClientCredentialParameters clientCredentialParam = ClientCredentialParameters.builder(
Collections.singleton(scope))
.build();
CompletableFuture<IAuthenticationResult> future = app.acquireToken(clientCredentialParam);
IAuthenticationResult result = future.get();
return result.accessToken();
De API aanroepen
Een verifieerbare referentie uitgeven of verifiëren:
Maak een HTTP POST-aanvraag naar de REST API van de aanvraagservice. De tenant-id is niet meer nodig in de URL omdat deze aanwezig is als een claim in het toegangstoken.
Probleem
POST https://verifiedid.did.msidentity.com/v1.0/verifiableCredentials/createIssuanceRequest
Verifieer
POST https://verifiedid.did.msidentity.com/v1.0/verifiableCredentials/createPresentationRequest
Koppel het toegangstoken als bearer-token aan de autorisatieheader in een HTTP-aanvraag.
Authorization: Bearer <token>
Stel de Content-Type
-header in op Application/json
.
Bereid en koppel de -uitgifte of -presentatie-verzoekpayload aan de body van het verzoek.
Dien de aanvraag in bij de REST API van de aanvraagservice.
De AANVRAAGservice-API retourneert een HTTP-statuscode 201 Created
bij een geslaagde aanroep. Als de API-aanroep een fout retourneert, raadpleeg dan de documentatie voor foutreferenties .
Voorbeeld van uitgifteaanvraag
Het volgende voorbeeld toont een aanvraag voor het uitgeven van verifieerbare referentiegegevens. Zie de uitgiftespecificatie van de aanvraagservice REST API in voor meer informatie over de payload.
POST https://verifiedid.did.msidentity.com/v1.0/verifiableCredentials/createIssuanceRequest
Content-Type: application/json
Authorization: Bearer <token>
{...JSON payload...}
Uitgifteaanvraag via het idTokenHint
attestation-proces.
{
"authority": "did:web:verifiedid.contoso.com",
"callback": {
"url": "https://contoso.com/api/issuer/issuanceCallback",
"state": "de19cb6b-36c1-45fe-9409-909a51292a9c",
"headers": {
"api-key": "OPTIONAL API-KEY for CALLBACK EVENTS"
}
},
"registration": {
"clientName": "Verifiable Credential Expert Sample"
},
"type": "VerifiedCredentialExpert",
"manifestUrl": "https://verifiedid.did.msidentity.com/v1.0/00001111-aaaa-2222-bbbb-3333cccc4444/verifiableCredentials/contracts/VerifiedCredentialExpert1",
"pin": {
"value": "3539",
"length": 4
},
"claims": {
"given_name": "Megan",
"family_name": "Bowen"
}
}
Verzoek om uitgifte met de idTokenHint
-attestatiestroom waarbij de vervaldatum instelt.
{
"authority": "did:web:verifiedid.contoso.com",
"callback": {
"url": "https://contoso.com/api/issuer/issuanceCallback",
"state": "de19cb6b-36c1-45fe-9409-909a51292a9c",
"headers": {
"api-key": "OPTIONAL API-KEY for CALLBACK EVENTS"
}
},
"registration": {
"clientName": "Verifiable Credential Expert Sample"
},
"type": "VerifiedCredentialExpert",
"manifestUrl": "https://verifiedid.did.msidentity.com/v1.0/00001111-aaaa-2222-bbbb-3333cccc4444/verifiableCredentials/contracts/VerifiedCredentialExpert1",
"pin": {
"value": "3539",
"length": 4
},
"claims": {
"given_name": "Megan",
"family_name": "Bowen"
},
"expirationDate": "2024-12-31T23:59:59.000Z"
}
Zie een van de volgende codevoorbeelden voor de volledige code:
Voorbeeld van presentatieaanvraag
In het volgende voorbeeld ziet u een verifieerbare presentatieaanvraag voor referenties. Zie Application Service REST API-presentatiespecificatievoor meer informatie over de nettolading.
POST https://verifiedid.did.msidentity.com/v1.0/verifiableCredentials/createPresentationRequest
Content-Type: application/json
Authorization: Bearer <token>
{...JSON payload...}
Presentatieaanvraag voor een referentiebewijs met een bepaald type en uitgever.
{
"authority": "did:web:verifiedid.contoso.com",
"callback": {
"url": "https://contoso.com/api/verifier/presentationCallback",
"state": "92d076dd-450a-4247-aa5b-d2e75a1a5d58",
"headers": {
"api-key": "OPTIONAL API-KEY for CALLBACK EVENTS"
}
},
"registration": {
"clientName": "Veritable Credential Expert Verifier"
},
"includeReceipt": true,
"requestedCredentials": [
{
"type": "VerifiedCredentialExpert",
"purpose": "So we can see that you a veritable credentials expert",
"acceptedIssuers": [
"did:web:verifiedid.contoso.com"
],
"configuration": {
"validation": {
"allowRevoked": true,
"validateLinkedDomain": true
}
}
}
]
}
Presentatieaanvraag met claimbeperkingen:
{
"authority": "did:web:verifiedid.contoso.com",
"includeReceipt": false,
"registration": {
"clientName": "Contoso Job Application Center",
"purpose": "Provide proof of attended courses"
},
"callback": {
"url": "https://contoso.com/api/verifier/presentationCallback",
"state": "92d076dd-450a-4247-aa5b-d2e75a1a5d58",
"headers": {
"api-key": "OPTIONAL API-KEY for CALLBACK EVENTS"
}
},
"requestedCredentials": [
{
"type": "FabrikamCourseCertification",
"acceptedIssuers": [ "did:web:learn.fabrikam.com" ],
"constraints": [
{
"claimName": "courseCode",
"values": ["FC100", "FC110", "FC150"],
},
{
"claimName": "courseTitle",
"contains": "network",
}
],
"configuration": {
"validation": {
"allowRevoked": false,
"validateLinkedDomain": true
}
}
}
]
}
Presentatieaanvraag met FaceCheck. Wanneer u FaceCheck gebruikt, moet de includeReceipt
onwaar zijn omdat het ontvangstbewijs dan niet wordt ondersteund.
{
"authority": "did:web:verifiedid.contoso.com",
"includeReceipt": false,
"registration": {
"clientName": "Contoso Job Application Center",
"purpose": "Provide proof of attended courses"
},
"callback": {
"url": "https://contoso.com/api/verifier/presentationCallback",
"state": "92d076dd-450a-4247-aa5b-d2e75a1a5d58",
"headers": {
"api-key": "OPTIONAL API-KEY for CALLBACK EVENTS"
}
},
"requestedCredentials": [
{
"type": "VerifiedEmployee",
"acceptedIssuers": [ "did:web:learn.contoso.com" ],
"configuration": {
"validation": {
"allowRevoked": false,
"validateLinkedDomain": true,
"faceCheck": {
"sourcePhotoClaimName": "photo",
"matchConfidenceThreshold": 70
}
}
}
}
]
}
Zie een van de volgende codevoorbeelden voor de volledige code:
Callback-gebeurtenissen
De verzoekpayload bevat de uitgifte en de presentatie callback-URL. Het eindpunt maakt deel uit van uw webtoepassing en moet openbaar beschikbaar zijn via het HTTPS-protocol. De Api voor aanvraagservice roept uw eindpunt aan om uw app te informeren over bepaalde gebeurtenissen. Dergelijke gebeurtenissen kunnen bijvoorbeeld zijn wanneer een gebruiker de QR-code scant, de dieptekoppeling naar de verificator-app gebruikt of het presentatieproces voltooit.
In het volgende diagram wordt de aanroep van uw app beschreven in de REST API van de aanvraagservice en de callbacks naar uw toepassing.
Configureer uw eindpunt om te luisteren naar binnenkomende HTTP POST-aanvragen. In het volgende codefragment ziet u hoe u de HTTP-aanvraag voor de uitgifteaanroep kunt verwerken en hoe u de gebruikersinterface dienovereenkomstig bijwerkt:
Niet van toepassing. Kies een van de andere programmeertalen.
[HttpPost]
public async Task<ActionResult> IssuanceCallback()
{
try
{
string content = new System.IO.StreamReader(this.Request.Body).ReadToEndAsync().Result;
_log.LogTrace("callback!: " + content);
JObject issuanceResponse = JObject.Parse(content);
// More code here
if (issuanceResponse["code"].ToString() == "request_retrieved")
{
var cacheData = new
{
status = "request_retrieved",
message = "QR Code is scanned. Waiting for issuance...",
};
_cache.Set(state, JsonConvert.SerializeObject(cacheData));
// More code here
}
}
Voor de volledige code, zie de code voor uitgifte en presentatie op de GitHub-opslagplaats.
mainApp.app.post('/api/issuer/issuance-request-callback', parser, async (req, res) => {
var body = '';
req.on('data', function (data) {
body += data;
});
req.on('end', function () {
requestTrace( req );
console.log( body );
var issuanceResponse = JSON.parse(body.toString());
var message = null;
if ( issuanceResponse.code == "request_retrieved" ) {
message = "QR Code is scanned. Waiting for issuance to complete...";
}
if ( issuanceResponse.code == "issuance_successful" ) {
message = "Credential successfully issued";
}
if ( issuanceResponse.code == "issuance_error" ) {
message = issuanceResponse.error.message;
}
// More code here
res.send()
});
res.send()
})
@app.route("/api/issuer/issuance-request-callback", methods = ['POST'])
def issuanceRequestApiCallback():
if request.headers['api-key'] != apiKey:
return Response( jsonify({'error':'api-key wrong or missing'}), status=401, mimetype='application/json')
issuanceResponse = request.json
if issuanceResponse["code"] == "request_retrieved":
cacheData = {
"status": issuanceResponse["code"],
"message": "QR Code is scanned. Waiting for issuance to complete..."
}
cache.set( issuanceResponse["state"], json.dumps(cacheData) )
return ""
if issuanceResponse["code"] == "issuance_successful":
cacheData = {
"status": issuanceResponse["code"],
"message": "Credential successfully issued"
}
cache.set( issuanceResponse["state"], json.dumps(cacheData) )
return ""
if issuanceResponse["code"] == "issuance_error":
cacheData = {
"status": issuanceResponse["code"],
"message": issuanceResponse["error"]["message"]
}
cache.set( issuanceResponse["state"], json.dumps(cacheData) )
return ""
return ""
@RequestMapping(value = "/api/issuer/issue-request-callback", method = RequestMethod.POST, produces = "application/json", consumes = "application/json")
public ResponseEntity<String> issueRequestCallback( HttpServletRequest request
, @RequestHeader HttpHeaders headers
, @RequestBody String body ) {
ObjectMapper objectMapper = new ObjectMapper();
try {
if ( !request.getHeader("api-key").equals(apiKey) ) {
lgr.info( "api-key wrong or missing" );
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body( "api-key wrong or missing" );
}
JsonNode presentationResponse = objectMapper.readTree( body );
String code = presentationResponse.path("code").asText();
ObjectNode data = null;
if ( code.equals( "request_retrieved" ) ) {
data = objectMapper.createObjectNode();
data.put("message", "QR Code is scanned. Waiting for issuance to complete..." );
}
if ( code.equals("issuance_successful") ) {
data = objectMapper.createObjectNode();
data.put("message", "Credential successfully issued" );
}
if ( code.equals( "issuance_error" ) ) {
data = objectMapper.createObjectNode();
data.put("message", presentationResponse.path("error").path("message").asText() );
}
if ( data != null ) {
data.put("status", code );
cache.put( presentationResponse.path("state").asText(), objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(data) );
}
} catch (java.io.IOException ex) {
ex.printStackTrace();
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body( "Technical error" );
}
return ResponseEntity.ok().body( "{}" );
}
Voor de volledige code, zie de code voor uitgifte en presentatie op de GitHub-opslagplaats.
Volgende stappen
Meer informatie over deze specificaties: