From the fields above, I don't know what to enter in customKeyIdentifier. I also can't find any example on how to create an application with a password. I tried using POST https://graph.microsoft.com/beta/applications. The body I'm using is this:
{
"displayName": "My App"
"passwordCredentials": [
{
"endDateTime": "2020-01-01T00:00:00Z",
"keyId": "5ac1014f-bfdd-49db-8a7c-e4bc48658e03",
"startDateTime": "2014-01-01T00:00:00Z",
"secretText": "5ac1014f-bfdd-49db-8a7c-e4bc48658e02",
"hint": "dffd"
}
]
}
Could you please provide me a way to create an application with a password?
âš Do not edit this section. It is required for docs.microsoft.com âžź GitHub issue linking.
@sureshja is there any chance you can take a look at this please?
I found this and I'll try it now; https://stackoverflow.com/questions/51353442/how-to-create-msa-id-password-for-creating-azure-bot-service-using-sdk/51359149#51359149
However I think that it is pretty hard to understand what each field is supposed to be from the documentation, which is still an issue.
@lleonard-msft ?
FYI, the stack overflow didn't work for me. Here is an example request for POST beta/applications
{
"allowPublicClient": true,
"displayName": "ccastrotestgraphapi",
passwordCredentials:
[{
"customKeyIdentifier": null,
"endDateTime": "2019-07-01T00:00:00Z",
"keyId": "b5b2920e-a47c-43b7-91ef-25ae96fddddd",
"startDateTime": "2018-07-01T00:00:00Z",
"secretText": "123QWER!@#$qwweerzcxv2",
"hint": "string"
}]
}
and I get an error that does not help me much as I don't know which piece of my request is wrong, and the documentation does not state the range or meaning of these fields.
{
"error": {
"code": "Request_BadRequest",
"message": "One or more property values specified are invalid.",
"innerError": {
"request-id": "390574a1-fa22-4586-b73c-87aa051ec0b9",
"date": "2018-12-10T18:31:22"
}
}
}
you should use the PATCH verb and the also add the line
"@odata.context": "https://graph.microsoft.com/beta/$metadata#applications/$entity", to your json object in the request body.
the URL you'll send the request to must be https://graph.microsoft.com/beta/applications/{id}
make sure to replace the {id} part with the id of your application. The id not the appId! You can get it by listing all applications via the graph.
Small note: a single application can only have a maximum of 3 application passwords it seems as trying to add more will result in an error
Do if you have 3 password credentials how do you patch to remove or replace one ? Use an existing KeyID? Where is the “3 application passwords fact documented”?
Well I tried adding more than 3 password and got an error from the graph api
Good to know, if guess one can validate vs what’s possible via the portal as behaviors should align?
Any idea how to remove or replace a passwordCredential?
@suddenelfilio Thanks for your response, I just came back to this.
You mentioned above "you should use the PATCH verb and the also add the line". However, it sounds inefficient to make 2 network calls to operate on the same object. Is this because it was designed to work this way, or because the POST does not work correctly?
Thanks!
@stevengum fyi
As an aside, you can create 100’s of password credentials - I know as I’ve done it.
You need to supply null as the keyId or you will get a bad request.
If you do a GET after the PATCH you can diff and get the keyId of you new key credential.
If you PATCH and keyId is not null the secret value must be null and vice versa.
I don’t think the API supports POST at all.
If you PATCH and keyId is not null the secret value must be null and vice versa.
When calling the v1 API, if keyId is _not null_ while value _is null_, the API seems to return Bad Request indicating updating an already-existing Secret is not allowed. (as a Patch against /applications/id with just passwordCredentials: [ ..existingCredsPlusNew ] in the body).
The only way I can make this call succeed is by passing only the new PasswordCredential in a single-item array, which clears all previous ones.
@JoeBrockhaus yes you can’t change the secret of an existing key credential (value not null, keyId is null) it seems.
You can add a new one, but to retain the old one(s) you need to supply the prior keyId (not null) and value (null).
Essentially one can use this to rotate keys. I wrote an function to:
This is deployed in a continuous web job rotating the keys every hour. Client code pulls the key from the KeyVault (with known permissioned credentials) to authenticate against the application.
Hope that is useful. Can’t share code as it is client work product.
Also @carlosscastro per original question, I was able to set/get the customKeyIdentifier by converting a string in to an ASCII byte array, and base64 encoding it, and in reverse de-base64 encoding and reconstruct the string from the ASCII byte array...
One can play tricks with JSON serialization/de-serialization converters to do this for you.
It's worth noting that I'm using the azure-graph npm package, so there may be some differences in endpoints/behavior, since I believe that operates against the v1 api.
Reviewing the Application API documentation, it appears a PATCH is supported.
The passwordCredentials Declared Property indicates support for POST, GET, PATCH.
I determined that the version of the (older) azure-graph client I'm using malforms startDate and endDate on incoming vs outgoing requests, so the API interprets these as invalid (update) operations against any already-existing passwordCredential objects.
// throws a 400 error response body
const graphClient = new GraphRbacManagementClient(serviceClientCredentials, tenantId);
const existingCreds = await graphClient.applications.listPasswordCredentials('app-object-id');
const resp = await graphClient.applications.updatePasswordCredentialsWithHttpOperationResponse('app-object-id', [ ...existingCreds, { ...newCred } ] );
if (resp.statusCode != '204'){ throw new Error(JSON.stringify(resp.body)); }
If I make these requests manually, everything works as expected.
const _ = require('lodash');
const bb = require('bluebird');
const { base64encode } = require('nodejs-base64');
const GraphRbacManagementClient = require('azure-graph').GraphRbacManagementClient;
// https://docs.microsoft.com/en-us/javascript/api/ms-rest/urlbasedrequestprepareoptions?view=azure-node-latest
class UrlBasedRequestPrepareOptions {
constructor(method, url, body) {
this.method = method;
this.url = url;
this.body = body;
}
};
async addAzureAdApplicationPasswordCredential(serviceClientCredentials, tenantId, appObjectId, secret, customKeyIdentifier, startDate, endDate) {
const graphClient = new GraphRbacManagementClient(credentials, tenantID);
const passwordCredentialsResp = await graphClient.sendRequest(new UrlBasedRequestPrepareOptions(
'GET', `https://graph.windows.net/${graphClient.tenantID}/applications/${appObjectId}/passwordCredentials?api-version=1.6`));
const updatedCredentials = passwordCredentialsResp.value
.map((cred) => ({
startDate: cred.startDate, endDate: cred.endDate,
keyId: cred.keyId, customKeyIdentifier: cred.customKeyIdentifier,
value: null
}));
const customKeyIdEncoded = _.isNil(customKeyIdentifier) ? customKeyIdentifier : base64encode(customKeyIdentifier);
updatedCredentials.push({
startDate: startDate, endDate: endDate,
value: secret,
customKeyIdentifier: customKeyIdEncoded
});
const passwordCredentialsPatchBody = { value: updatedCredentials };
const reqOptions = new UrlBasedRequestPrepareOptions(
'PATCH', `https://graph.windows.net/${graphClient.tenantID}/applications/${appObjectId}/passwordCredentials?api-version=1.6`,
passwordCredentialsPatchBody );
const resp = await (graphClient
.sendRequest(reqOptions)
.catch((error) => {
if (error) { log.error(error); }
})
// delay a bit to avoid 503 'concurrent request against tenant' error.
.then(() => bb.delay(1500))
.then(() =>
graphClient.applications.listPasswordCredentials(appObjectId))
);
return _.isNil(customKeyIdentifier) ? resp
: _.find(resp, (x) => x.customKeyIdentifier == customKeyIdEncoded);
}
Yes I’ve been using the Beta API as recommended by a MS Azure consultant...
Albeit it won’t be beta at some point and endpoints will need to change.
@JoeBrockhaus you're using the old Active Directory Graph API (host is https://graph.windows.net), not MS Graph (host is https://graph.microsoft.com).
I'm also unable to create a password via the REST API, whether in the initial POST or a later PATCH. The passwordCredentials field I'm passing is
{
"customKeyIdentifier": "awBlAHkAMQA=", // base64/UTF16-LE encoded "key1"
"endDateTime": "2020-03-06T22:18:32Z",
"secretText": "<some secret value blah blah>"
}
The error I get each time is Property passwordCredentials in payload has a value that does not match schema.
The same request works fine in AD Graph, with the secretText field name changed to value.
Nevermind, I figured it out. I was sending a single password credential object, but passwordCredentials expects an array of such objects. The API seems to be working correctly.
For reference, the basic request body to create an app with a password is
{
"displayName": "yourAppName",
"passwordCredentials": [
{
"customKeyIdentifier": "base64/UTF16-encoded-name",
"endDateTime": "2020-12-31T12:00:00Z",
"secretText": "some-secret-text"
}
]
}
FYI - the PATCH behavior deems to have changed. We get a 400 - Bad Request response the t has worked fine for over 10 months!
{
"passwordCredentials":
[
{
"customKeyIdentifier":"base64/UTF16-encoded-name",
"endDateTime": "2019-11-02T17:32:09.9638578+00:00",
"keyId": null,
"startDateTime": null,
"secretText": "some-secret-text",
"hint": null,
"displayName":"NewKey-01-Nov-2019 10:32:09"
}
]
}
The expectation is that the keyId will be set once it's created, and that until now startDateTime did not need to be specified, and neither did hint - such that existing hints are maintained from a prior request of existing keys.
Anyone else seeing issues ?
Yep, also seeing an error trying to create a new app:
Bad request (400).
New password credentials must be generated with service actions.
Hi @Hong-Revo where do you get the more specific error - I looked in the HTTP Response and don't see anything similar - New password credentials must be generated with service actions
What are service actions?
In order to increase the security profile of the applications, MSGraph blocks developers setting custom passwords. The addPassword method (a.k.a service actions) generate strong passwords for them.
@carlosscastro, Have you looked at the example from this doc reference? https://docs.microsoft.com/en-us/graph/api/application-addpassword?view=graph-rest-beta&tabs=http
You need not provide a POST body unless you want to have a friendly displayName set to a password for identification later.
@sureshja Thanks. It looks like the app/service principal API is now in the 1.0 API as well, are there any bits still in beta that haven't been finalised?
Also... that call creates a new password, but how do you update an existing one (eg if it's about to expire)? I tried calling addPassword with the keyId of an existing password, but it created a new one instead. Are we just supposed to delete old passwords as they expire?
Hi @sureshja where is a good place to find release notes of the graph api changes.
Microsoft documentation is a bit of a maze and having the ability to subscribe to change notification would be very valuable as this change has caught us off guard.
Thanks!
@sureshja how is one supposed to set the PaswordCredential:CustomKeyIdentifier?
As a PATCH after the addPassword?
I have Application Owner permissions (per portal) and assume this results in/includes Application.ReadWrite.OwnedBy permission on the application, and get a 405 Method Not Allowed error?
@Hong-Revo - Applications is in v1.0. ServicePrincipal is in the final stages of development before I get to v1.0. As we block setting or updating passwords using POST or PATCH, developers should take advantage of addPassword and removePassword to rotate secrets.
@acds - customKeyIdentifier is deprecated. If you want to set a friendly name to the secrets you are adding, please use displayName instead. If you are setting secrets as part of a POST operation at app creation, you will want to separate the secret creation in a subsequent POST call to app creation using addPassword.
Thanks @sureshja, do you know roughly when servicePrincipal will be in 1.0?
It seems as if this issue has been resolved. the add and remove password APIs are now available in both beta and v1.0. No change identified for the page. Closing the issue.
Most helpful comment
@Hong-Revo - Applications is in v1.0. ServicePrincipal is in the final stages of development before I get to v1.0. As we block setting or updating passwords using POST or PATCH, developers should take advantage of addPassword and removePassword to rotate secrets.