Authorization failure when creating a Stream Analytics job - azure

I've been trying (and failing) to create an Azure Stream Analytics job programatically. I was following this example originally:
https://azure.microsoft.com/en-gb/documentation/articles/stream-analytics-dotnet-management-sdk/
But it pops up a dialog for you to log in. I want to be able to do this server side. It looks like I need to use Azure AD to use the Resource Manager APIs. I've been working my way through this:
https://msdn.microsoft.com/en-us/library/azure/dn790557.aspx#bk_portal
And the code looks like this:
var authContext = new AuthenticationContext("https://login.microsoftonline.com/{tenant id}/oauth2/token");
var clientId = "{app client id}";
var appKey = "{app key}";
var subscriptionId = "{subscription id}";
var clientCredential = new ClientCredential(clientId, appKey);
var result = authContext.AcquireToken("https://management.core.windows.net/", clientCredential);
var creds = new TokenCloudCredentials(subscriptionId, result.AccessToken);
var client = new StreamAnalyticsManagementClient(creds);
var jobCreateParameters = new JobCreateOrUpdateParameters
{
Job = new Job
{
Name = streamAnalyticsJobName,
Location = "North Europe",
Properties = new JobProperties
{
EventsOutOfOrderPolicy = EventsOutOfOrderPolicy.Adjust,
Sku = new Sku
{
Name = "Standard"
}
}
}
};
var jobCreateResponse = client.StreamingJobs.CreateOrUpdate(resourceGroupName, jobCreateParameters);
I can successfully acquire a token, but creating the job fails:
AuthorizationFailed: The client 'REDACTED' with object id 'REDACTED' does not have authorization to perform action 'Microsoft.StreamAnalytics/streamingjobs/write' over scope '/subscriptions/REDACTED/resourcegroups/REDACTED/providers/Microsoft.StreamAnalytics/streamingjobs/REDACTED'
Am I doing something wrong? The app has the delegated permissions set.

UPDATE - 08-Dec-2015
There's an easy way to assign roles to Service Principals now. Please see this link for more details: https://azure.microsoft.com/en-in/documentation/articles/resource-group-create-service-principal-portal/.
Original Response
When you grant access to an application to your Azure subscription, behind the scenes a user is created with Service Principal user type in your Azure AD. The code you're using below assumes that you're using this Service Principal user when getting access token.
var clientCredential = new ClientCredential(clientId, appKey);
var result = authContext.AcquireToken("https://management.core.windows.net/", clientCredential);
var creds = new TokenCloudCredentials(subscriptionId, result.AccessToken);
However by default this user is not granted any permissions (RBAC) on your subscription and that's why you're getting the authorization error.
To solve this problem, what you would need to do is grant appropriate role to this user in your subscription. Now you can use PowerShell to do so or you can do it via code using ADAL library + making some web requests.
What I did was I made use of ADAL library to get access tokens and then used Google Postman (or Fiddler) to do other stuff. In my case, it was a web application. Here's what I did:
I logged in into the application as Global Administrator (Subscription Owner) and got a code. Using that code and ADAL library, I got the access token (let's call it token1).
var authContext = new AuthenticationContext(string.Format("{0}/common", signinEndpoint));//signinEndpoint = https://login.windows.net
var result = await authContext.AcquireTokenByAuthorizationCodeAsync(code, redirectUri, credential);
I copied the tenant id and access token returned to me in result above.
Next thing I did was I found out the object id of the Service Principal user using POSTMAN. This is the GET URL I executed there. For Authorization header, you would need to use Bearer {token1}.
https://graph.windows.net/{subscription-id}/servicePrincipals?api-version=1.5&$filter=appId eq '{app-client-id}'
After that I acquired another access token (let's call it token2) for Service Management API operation using the code below:
authContext = new AuthenticationContext(string.Format("{0}/{1}", signinEndpoint, result.TenantId));
result = await authContext.AcquireTokenSilentAsync(serviceManagementApiEndpoint, credential, new UserIdentifier(request.UserInfo.UniqueId, UserIdentifierType.UniqueId));//serviceManagementApiEndpoint = https://management.core.windows.net/
After that I listed the roles in my subscription and picked the role I wanted to assign to my Service Principal user. In my case, I wanted to assign a Reader role so I noted down the role's id. For Authorization header, you would need to use Bearer {token2}.
https://management.azure.com/subscriptions/{subscription-id}/providers/Microsoft.Authorization/roleDefinitions?api-version=2015-06-01
Next is assignment of this role to the user. For this I created a guid as role assignment id and used the following URL:
https://management.azure.com/subscriptions/{subscription-id}/providers/microsoft.authorization/roleassignments/{role-assignment-id}?api-version=2015-06-01
It's going to be a PUT request and this was the request body would be something like:
{
"properties":
{
"roleDefinitionId": "{role id of the selected role from step 5}",
"principalId": "{id of the service principal from step 3"
}
}
Please ensure that the content-type of the request is set as application/json;odata=verbose and not application/json.
That's pretty much it! After that your code should work just fine :)
Give it a try and see what happens.

Related

Authenticate to multiple Azure services using Service Principle (.net Core)

I need to get access to Key Vault and Service Bus from code, using a Service Principle for authentication.
I can use the following code to access Service Bus, which works as expected - when I enable to Service Principle in the Access Policies I can pull the list of topics:
var credentials = SdkContext.AzureCredentialsFactory.FromServicePrincipal(APPID, APPSECRET, TENANTID, AzureEnvironment.AzureGlobalCloud);
var serviceBusManager = ServiceBusManager.Authenticate(credentials, SUBSCRIPTIONID);
var serviceBusNamespace = serviceBusManager.Namespaces.List().SingleOrDefault(n => n.Name == "SERVICEBUSNAMESPACE");
var topics = serviceBusNamespace.Topics.ListAsync().GetAwaiter().GetResult();
However, I also need to get some information from Key Vault and I was trying to establish a common way to authenticate.
METHOD 1
Similar to the above, I tried this code to access KeyVault:
var credentials = SdkContext.AzureCredentialsFactory.FromServicePrincipal(APPID, APPSECRET, TENANTID, AzureEnvironment.AzureGlobalCloud);
var kvManager = new KeyVaultClient(credentials);
var secret = kvManager.GetSecretAsync("https://VAULTNAMESPACE.vault.azure.net", "SECRETNAME").GetAwaiter().GetResult().Value;
I get the the following error:
Microsoft.Azure.KeyVault.Models.KeyVaultErrorException: 'Operation
returned an invalid status code 'Unauthorized''
METHOD 2
This code does work for Key Vault however (showing I have correct permissions):
string GetSecret()
{
var client = new KeyVaultClient(GetAccessToken);
var secret = client.GetSecretAsync("https://VAULTNAMESPACE.vault.azure.net", "SECRETNAME").GetAwaiter().GetResult();
return secret;
}
private static async Task<string> GetAccessToken(string authority, string resource, string scope)
{
var context = new AuthenticationContext("https://login.windows.net/" + tenantId);
var credential = new ClientCredential(appId, appSecret);
var tokenResult = await context.AcquireTokenAsync("https://vault.azure.net", credential);
return tokenResult.AccessToken;
}
But, again, it's a very KeyVault specific way to Authenticate and I was hoping to establish a common mechanism using SdkContext.AzureCredentialsFactory. Any reason why I'd be getting an Unauthorized exception with the code above connecting to Key Vault? (all is set up correctly in Azure).
Thanks for any tips!
When you use SdkContext.AzureCredentialsFactory.FromServicePrincipal to authenticate, it will use https://management.azure.com/ as its Resource Uri.
While Azure Key Vault has its own authorization system and its Resource Uri is https://vault.azure.net, so you may get the Unauthorized error message.
So, you could use Method2 to get access to Azure Key Vault with right Resource Uri.
For more details, you could refer to this article.

How to call Microsoft Graph from console application c#

I need to call Microsoft Graph API to create user in Azure AD.
First I need to test from console application and then need to implement in Azure function.
https://developer.microsoft.com/en-us/graph/graph-explorer
I am new to Microsoft Graph API , How can I connect and execute API from c# console application.
I have already registered the application in AAD.
I am trying to acquire token as :
string resourceId = "https://graph.microsoft.com";
string tenantId = "<tenantID>";
string authString = "https://login.microsoftonline.com/" + tenantId;
string upn = String.Empty;
string clientId = "<ClientID>";
string clientSecret = "<clientSecret>";
//string clientSecret = ConfigurationManager.AppSettings["clientSecret"];
log.Verbose("ClientSecret=" + clientSecret);
log.Verbose("authString=" + authString);
var authenticationContext = new AuthenticationContext(authString, false);
// Config for OAuth client credentials
ClientCredential clientCred = new ClientCredential(clientId, clientSecret);
AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenAsync(resourceId,clientCred);
string token = authenticationResult.AccessToken;
log.Verbose("token=" + token);
I trying to use existing AADB2C.
b2c-extensions-app. Do not modify. Used by AADB2C for storing user data.
I have enabled permission as:
I neither get exception nor get access token and program silently exit
Also :
There is new library
<package id="Microsoft.Identity.Client" version="1.1.0-preview" targetFramework="net46" />
How can I direct login without login pop-up with the following and acquire token ?
PublicClientApplication
I assume that you already have Azure AD application with granted Administrative Consent.
In order to connect from a console app, you'll need to first obtain a valid token. Since you lack a UI, you'll want to Get access without a user. Note that this type of "app-only" token requires Administrative Consent before it can be used.
Then you have to add two NuGet dependencies to your dotnet project
<PackageReference Include="Microsoft.Graph" Version="1.15.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.0.0" />
Microsoft.Identity.Client for authentication using Azure AD and Microsoft.Graph for executing MS Graph queries.
var tenantId = "you-azure-tenand-id";
var clientId = "azure-ad-application-id";
var clientSecret = "unique-secret-generated-for-this-console-app";
// Configure app builder
var authority = $"https://login.microsoftonline.com/{tenantId}";
var app = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithClientSecret(clientSecret)
.WithAuthority(new Uri(authority))
.Build();
// Acquire tokens for Graph API
var scopes = new[] {"https://graph.microsoft.com/.default"};
var authenticationResult = await app.AcquireTokenForClient(scopes).ExecuteAsync();
// Create GraphClient and attach auth header to all request (acquired on previous step)
var graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(requestMessage => {
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("bearer", authenticationResult.AccessToken);
return Task.FromResult(0);
}));
// Call Graph API
var user = await graphClient.Users["Me#domain.com"].Request().GetAsync()
Update 2020.01
There is a new package Microsoft.Graph.Auth that simplify auth and token management.
Let's say you want to use some Beta API this time.
<PackageReference Include="Microsoft.Graph.Auth" Version="1.0.0-preview.2" />
<PackageReference Include="Microsoft.Graph.Beta" Version="0.12.0-preview" />
var tenantId = "you-azure-tenand-id";
var clientId = "azure-ad-application-id";
var clientSecret = "unique-secret-generated-for-this-console-app";
// Configure application
var clientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantId)
.WithClientSecret(clientSecret)
.Build();
// Create ClientCredentialProvider that will manage auth token for you
var authenticationProvider = new ClientCredentialProvider(clientApplication);
var graphClient = new GraphServiceClient(authenticationProvider);
// Call Graph API
var user = await graphClient.Users["Me#domain.com"].Request().GetAsync()
In order to connect from a console app, you'll need to first obtain a valid token. Since you lack a UI, you'll want to Get access without a user. Note that this type of "app-only" token requires Administrative Consent before it can be used.
In order to support the Create User scenario, you will need to ensure your permission scopes include User.ReadWrite.All.
Once you have a valid token you can make calls into the Graph API. Graph is a REST API so all calls are made over HTTP with the token passed within the Authorization Header.
You can read a general overview at Get started with Microsoft Graph and REST. There are also several language/framework specific overviews available but all of them assume you have a UI (i.e. not simply console). Generally speaking, if you're looking for a console tool for creating users you may prefer using PowerShell.
This question is rather old, but it was one of the first questions that popped up when I initially needed to do the same thing. Below I will document the steps and resources I used to make it happen:
I used an O365 tenant (you can get one from office.com - note that you can get a one year developer trial). Once you have a tenant, you also have access to Azure portal if you log in as your tenant admin user. Under Azure Portal, go to Active Directory/properties to see the tenant ID.
I followed the instructions here https://learn.microsoft.com/en-us/azure/active-directory/develop/quickstart-v2-netcore-daemon to create a new registered application. I created a new secret and copied the value (that will be client secret in your console app). The registered application id will be the client ID in your console app.
I cloned the github repo in the above link and changed the values in the appsettings to the tenant ID, client ID, and client secret noted in the steps above.
The code in that repo has some methods called which no longer exist in ConfigurationBuilder as of .NETCore 2.1. I substituted these lines (there's probably a better / shorter way):
authenticationConfig.Tenant = Configuration.GetSection("Tenant").Value.ToString();
authenticationConfig.ClientId = Configuration.GetSection("ClientId").Value.ToString();
authenticationConfig.ClientSecret = Configuration.GetSection("ClientSecret").Value.ToString();
You should now be iterating through users in your tenant. You can go to the graph explorer ( https://developer.microsoft.com/en-us/graph/graph-explorer ) to find more URLs (find the line in Program.cs to substitute them). As far as I know so far, v2.0 of the API is "beta" (put "beta" where "v1.0" is - someone please correct me if I'm wrong).
await apiCaller.CallWebApiAndProcessResultASync("https://graph.microsoft.com/v1.0/users", result.AccessToken, Display);
This MSAL console app tutorial describes getting a token using MSAL (Microsoft Authentication Library) in a .NET console app.
To make a Microsoft Graph call, I replaced the RunAsync() function
with this, which attaches the acquired token to the requests with the
GraphServiceClient:
static async Task RunAsync()
{
const string clientId = "your client id";
string[] scopes = { "User.Read" };
AuthenticationResult result;
var clientApp = new PublicClientApplication(clientId);
try
{
result = await clientApp.AcquireTokenAsync(scopes.Split(new char[] { ' ' }));
Console.WriteLine(result.AccessToken);
GraphServiceClient graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(
async (requestMessage) =>
{
// Append the access token to the request.
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", result.AccessToken);
// Some identifying header
requestMessage.Headers.Add("SampleID", "aspnet-connect-sample");
}));
// Get a page of mail from the inbox
var inboxMail = await graphClient.Me.MailFolders.Inbox.Messages.Request().GetAsync();
foreach(var mail in inboxMail.CurrentPage.ToList())
{
Console.Write("From: {0}\nSubject: {1}\nBody:\n{2}\n--------------------\n",
mail.From.EmailAddress.Address, mail.Subject, mail.BodyPreview);
}
}
// Unable to retrieve the access token silently.
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}

Resource not found for the segment 'me'

i'm using Graph API to retrieve profile information of user who's currently logged in from the Azure AD, unfortunately i'm receiving the following error message :
{"odata.error":{"code":"Request_ResourceNotFound","message":{"lang":"en","value":"Resource not found for the segment 'me'."}}}
Below is my code :
Uri serviceRoot = new Uri(serviceRootURL);
ActiveDirectoryClient adClient = new ActiveDirectoryClient(
serviceRoot,
async () => await GetAppTokenAsync());
var user = (User)await adClient.Me
.Expand(x => x.Manager)
.ExecuteAsync();
And below is my code for GetAppTokenAsync() :
private static async Task<string> GetAppTokenAsync()
{
// Instantiate an AuthenticationContext for my directory (see authString above).
AuthenticationContext authenticationContext = new AuthenticationContext(authString, false);
// Create a ClientCredential that will be used for authentication.
// This is where the Client ID and Key/Secret from the Azure Management Portal is used.
ClientCredential clientCred = new ClientCredential(clientID, clientSecret);
// Acquire an access token from Azure AD to access the Azure AD Graph (the resource)
// using the Client ID and Key/Secret as credentials.
AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenAsync(resAzureGraphAPI, clientCred);
// Return the access token.
return authenticationResult.AccessToken;
}
From your code "await GetAppTokenAsync()" , you are getting an app-only token , which using application identity, instead of as a user's identity .
The "(User)await adClient.Me" won't work if that token is not associated with a user .
To use app token to get user manager information ,you need to specify the user you want to query , code below is for your reference :
try
{
User manager = (User)await adClient.Users.GetByObjectId("5eba8883-c258-45d0-8add-a286a1ec1e91").Manager.ExecuteAsync();
}
catch (Exception ex)
{
throw;
}
Update
You could use authorization code flow for delegated permissions(user's identity) . If you want a client library code sample , you could refer to this code sample . After user sign in , you could use below code to get manager of current login user :
ActiveDirectoryClient client = AuthenticationHelper.GetActiveDirectoryClient();
User manager = (User)await client.Me.Manager.ExecuteAsync();
I used an application identity with the legacy Azure Active Directory api and the 'Application.ReadWrite.OwnedBy' permission to work around the Resource not found for the segment 'me' error. The same permission exists in the Microsoft Graph api, but the behavior is not identical. More information here.

Application access to SharePoint Online using Azure AD Token

How can I get an application token to query SharePoint with application credentials (= without user impersonation) using Azure AD?
The following code works perfectly for querying data as a user but we need to fetch information without impersonation like listing all sites in the collection regardless of user permissions etc.
Exception thrown:
An exception of type
'Microsoft.IdentityModel.Clients.ActiveDirectory.AdalServiceException'
occurred in mscorlib.dll but was not handled in user code
Additional information: AADSTS70001: Application with identifier 'xxx'
was not found in the directory sharepoint.com
Code to get token:
internal static async Task<string> GetSharePointAccessToken(string url, string userAccessTokenForImpersonation)
{
string clientID = #"<not posted on stack overflow>";
string clientSecret = #"<not posted on stack overflow>";
var appCred = new ClientCredential(clientID, clientSecret);
var authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext("https://login.windows.net/common");
// Use user assetion if provided, otherwise use principal account
AuthenticationResult authResult = null;
if (string.IsNullOrEmpty(userAccessTokenForImpersonation))
{
authResult = await authContext.AcquireTokenAsync(new Uri(url).GetLeftPart(UriPartial.Authority), appCred);
}
else
{
authResult = await authContext.AcquireTokenAsync(new Uri(url).GetLeftPart(UriPartial.Authority), appCred, new UserAssertion(userAccessTokenForImpersonation));
}
return authResult.AccessToken;
}
Test code:
// Auth token from Bearer https://xxx.azurewebsites.net/.auth/me
string authHeader = #"<valid jwt bearer token from azure auth>";
var sharePointUrl = #"https://xxx.sharepoint.com/sites/testsite/";
string sharePrincipalToken = await GetSharePointAccessToken(sharePointUrl, null); // <-- doesn't work
string sharePointUserToken = await GetSharePointAccessToken(sharePointUrl, authHeader); // <-- works
Permissions in Azure AD:
The error message you are getting implies that you are signing in with a user that is pointing our token service to get a token in the context of "sharepoint.com"
This is because you are using the "common" endpoint. Read more about that here.
Instead try using a fixed endpoint, where the tenant is the same as where the application is registered and see if that solves your issue.
If your plan is to make this application accessible by multiple tenants, make sure that you have explicitly set your application to be multi-tenant, and then make sure you have a user from the external tenant try and sign into the application before you try doing service to service calls.
Let me know if this helps.

Sending IM with Skype for Business Online from Console App

I am trying to set up a C# console app that can send notifications/reminders to users via Skype for Business online from a generic AD account. I was excited to see the other day that according to this page, UCWA is now supported in Skype for Business online: https://msdn.microsoft.com/en-us/library/office/mt650889.aspx.
I've been trying to follow this tutorial to get this set up: https://msdn.microsoft.com/en-us/library/office/mt590891(v=office.16).aspx. So far I haven't really had much luck... I have my application set up in Azure AD but I get stuck at the "Requesting an access token using implicit grant flow" step of that article (not 100% certain I'm taking the correct actions before that either)... so far I have this:
string clientId = "xxxxxxxx"
string resourceUri = "https://webdir.online.lync.com";
string authorityUri = "https://login.windows.net/common/oauth2/authorize";
AuthenticationContext authContext = new AuthenticationContext(authorityUri);
UserCredential cred = new UserCredential("username", "password");
string token = authContext.AcquireToken(resourceUri, clientId, cred).AccessToken;
var poolReq = CreateRequest("https://webdir.online.lync.com/autodiscover/autodiscoverservice.svc/root", "GET",token);
var poolResp = GetResponse(poolReq);
dynamic tmp = JsonConvert.DeserializeObject(poolResp);
string resourcePool = tmp._links.user.href;
Console.WriteLine(resourcePool);
var accessTokenReq = CreateRequest("https://login.windows.net/common/oauth2/authorize"
+ "?response_type=id_token"
+ "&client_id=" + clientId
+ "&redirect_uri=https://login.live.com/oauth20_desktop.srf"
+ "&state=" + Guid.NewGuid().ToString()
+ "&resource=" + new Uri(resourcePool).Host.ToString()
, "GET",token);
var accessTokenResp = GetResponse(accessTokenReq);
my GetResponse and CreateRequest methods:
public static string GetResponse(HttpWebRequest request)
{
string response = string.Empty;
using (HttpWebResponse httpResponse = request.GetResponse() as System.Net.HttpWebResponse)
{
//Get StreamReader that holds the response stream
using (StreamReader reader = new System.IO.StreamReader(httpResponse.GetResponseStream()))
{
response = reader.ReadToEnd();
}
}
return response;
}
public static HttpWebRequest CreateRequest(string uri, string method, string accessToken)
{
HttpWebRequest request = System.Net.WebRequest.Create(uri) as System.Net.HttpWebRequest;
request.KeepAlive = true;
request.Method = method;
request.ContentLength = 0;
request.ContentType = "application/json";
request.Headers.Add("Authorization", String.Format("Bearer {0}", accessToken));
return request;
}
accessTokenResp is an office online logon page, not the access token I need to move forward... so I'm stuck. I've tried quite a few variations of the above code.
I've been scouring the net for more examples but can't really find any, especially since UCWA support for Office 365 is so new. Does anyone have an example of how to do what I am trying to do or can point me to one? Everything I've found so far hasn't really even been close to what I'm trying. I can't use the Skype for Business client SDK unfortunately either as it doesn't meet all of my requirements.
I came to a working solution using ADAL (v3), with the help of steps outlined at
Authentication using Azure AD
Here the steps, which involve requesting multiple authentication tokens to AAD using ADAL
Register your application, as Native Application, in Azure AD.
Perform autodiscovery to find user's UCWA root resource URI.
This can be done by performing a GET request on
GET https://webdir.online.lync.com/Autodiscover/AutodiscoverService.svc/root?originalDomain=yourdomain.onmicrosoft.com
Request an access token for the UCWA root resource returned in the autodiscovery response, using ADAL
For instance, your root resource will be at
https://webdir0e.online.lync.com/Autodiscover/AutodiscoverService.svc/root/oauth/user?originalDomain=yourdomain.onmicrosoft.com
you'll have to obtain a token from AAD for resource https://webdir0e.online.lync.com/
Perform a GET on the root resource with the bearer token obtained from ADAL
GET https://webdir0e.online.lync.com/Autodiscover/AutodiscoverService.svc/root/oauth/user?originalDomain=yourdomain.onmicrosoft.com
This will return, within the user resource, the URI for applications resource, where to create your UCWA application. This in my case is:
https://webpoolam30e08.infra.lync.com/ucwa/oauth/v1/applications
Residing then in another domain, thus different audience / resource, not included in the auth token previously obatained
Acquire a new token from AAD for the host resource where the home pool and applications resource are (https://webpoolam30e08.infra.lync.com in my case)
Create a new UCWA application by doing a POST on the applications URI, using the token obtained from ADAL
Voilá, your UCWA application is created. What I notice at the moment, is that just few resources are available, excluding me / presence. So users' presence can be retrieved, but self presence status can't be changed.
I've been able however to retrieve my personal note, and the following resources are available to me:
people
communication
meetings
Show me some code:
Function to perform the flow obtaining and switching auth tokens
public static async Task<UcwaApp> Create365UcwaApp(UcwaAppSettings appSettings, Func<string, Task<OAuthToken>> acquireTokenFunc)
{
var result = new UcwaApp();
result.Settings = appSettings;
var rootResource = await result.Discover365RootResourceAsync(appSettings.DomainName);
var userUri = new Uri(rootResource.Resource.GetLinkUri("user"), UriKind.Absolute);
//Acquire a token for the domain where user resource is
var token = await acquireTokenFunc(userUri.GetComponents(UriComponents.SchemeAndServer, UriFormat.SafeUnescaped));
//Set Authorization Header with new token
result.AuthToken = token;
var usersResult = await result.GetUserResource(userUri.ToString());
//
result.ApplicationsUrl = usersResult.Resource.GetLinkUri("applications");
var appsHostUri = new Uri(result.ApplicationsUrl, UriKind.Absolute).GetComponents(UriComponents.SchemeAndServer, UriFormat.SafeUnescaped);
//Acquire a token for the domain where applications resource is
token = await acquireTokenFunc(appsHostUri);
//Set Authorization Header with new token
result.AuthToken = token;
//
var appResult = await result.CreateApplicationAsync(result.ApplicationsUrl, appSettings.ApplicationId, appSettings.UserAgent, appSettings.Culture);
return result;
}
Usage code ato retrieve OAuth tokens using ADAL
var ucSettings = new UcwaAppSettings
{
UserAgent = "Test Console",
Culture = "en-us",
DomainName = "yourdomain.onmicrosoft.com",
ApplicationId = "your app client id"
};
var acquireTokenFunc = new Func<string, Task<OAuthToken>>(async (resourceUri) =>
{
var authContext = new AuthenticationContext("https://login.windows.net/" + ucSettings.DomainName);
var ar = await authContext.AcquireTokenAsync(resourceUri,
ucSettings.ApplicationId,
new UserCredential("myusername", "mypassword"));
return new OAuthToken(ar.AccessTokenType, ar.AccessToken, ar.ExpiresOn.Ticks);
});
var app = await UcwaApp.Create365UcwaApp(ucSettings, acquireTokenFunc);
It should be of course possible to avoid hard-coding username and password using ADAL, but this was easier for PoC and especially in case of Console Application as you asked
I've just blogged about this using a start-to-finish example, hopefully it will help you. I only go as far as signing in, but you can use it with another post I've done on sending IMs using Skype Web SDK here (see day 13 and 14) and combine the two, it should work fine.
-tom
Similar to Massimo's solution, I've created a Skype for Business Online C# based console app that demonstrates how to sign and use UCWA to create/list/delete meetings and change user presence. I haven't gotten around to extending it to send IM's, but you're certainly welcome to clone my repository and extend it to your needs. Just drop in your Azure AD tenant name and native app ID into the code.
I think they just turned this on today - I was doing something unrelated with the Skype Web SDK samples and had to create a new Azure AD app, and noticed that there are two new preview features for receiving conversation updates and changing user information.
Now everything in the Github samples works for Skype For Business Online.

Resources