WebForms app using OWIN/MSAL not receiving Authorization Code - vb.net

I have been tasked with modifying several WebForms apps by migrating them to MSAL v4. I have downloaded a working MVC C# example (msgraph-training-aspnetmvcapp) from GitHub and it runs flawlessly. I have managed to emulate the MVC example up to the point of initial token caching. The OWIN single-tenant sign-in process executes as expected; however, the Task assigned to handle notification receipt (OnAuthorizationCodeReceivedAsync) is never fired. Consequently, there is no token placed into the Session cache.
The OWIN middleware is instantiated at startup as follows:
Public Sub ConfigureAuth(ByVal app As IAppBuilder)
System.Diagnostics.Debug.WriteLine(vbLf & "Startup.Auth.vb ConfigureAuth() - STARTED" & vbLf)
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType)
app.UseCookieAuthentication(New CookieAuthenticationOptions())
app.UseOpenIdConnectAuthentication(New OpenIdConnectAuthenticationOptions With {
.ClientId = appId,
.Scope = $"openid email profile offline_access {graphScopes}",
.Authority = sAuthority,
.RedirectUri = redirectUri,
.PostLogoutRedirectUri = redirectUri,
.TokenValidationParameters = New TokenValidationParameters With {
.ValidateIssuer = False
},
.Notifications = New OpenIdConnectAuthenticationNotifications With {
.AuthenticationFailed = AddressOf OnAuthenticationFailedAsync,
.AuthorizationCodeReceived = AddressOf OnAuthorizationCodeReceivedAsync
}
})
System.Diagnostics.Debug.WriteLine(vbLf & "Startup.Auth.vb ConfigureAuth() - COMPLETED" & vbLf)
End Sub
Note that a pair of Notifications have been configured by OWIN, one to indicate successful Authorization Code acquisition (AuthorizationCodeReceived) and another to indicate authentication failure (AuthenticationFailed). Each is mapped to a corresponding asynchronous Task object. The Tasks are defined as follows:
Private Shared Function OnAuthenticationFailedAsync(ByVal notification As AuthenticationFailedNotification(Of OpenIdConnectMessage, OpenIdConnectAuthenticationOptions)) As Task
System.Diagnostics.Debug.WriteLine(vbLf & "Startup.Auth.vb OnAuthenticationFailedAsync()" & vbLf)
notification.HandleResponse()
Dim redirect As String = $"~/Views/ErrorPage?message={notification.Exception.Message}"
If notification.ProtocolMessage IsNot Nothing AndAlso Not String.IsNullOrEmpty(notification.ProtocolMessage.ErrorDescription) Then
redirect += $"&debug={notification.ProtocolMessage.ErrorDescription}"
End If
notification.Response.Redirect(redirect)
Return Task.FromResult(0)
End Function
Private Async Function OnAuthorizationCodeReceivedAsync(ByVal notification As AuthorizationCodeReceivedNotification) As Task
System.Diagnostics.Debug.WriteLine(vbLf & "Startup.Auth.vb OnAuthorizationCodeReceivedAsync()" & vbLf)
Dim signedInUser = New ClaimsPrincipal(notification.AuthenticationTicket.Identity)
Dim idClient As IConfidentialClientApplication = ConfidentialClientApplicationBuilder.Create(appId).WithRedirectUri(redirectUri).WithClientSecret(appSecret).Build()
Dim tokenStore As SessionTokenStore = New SessionTokenStore(idClient.UserTokenCache, HttpContext.Current, signedInUser)
Try
Dim scopes As String() = graphScopes.Split(" "c)
Dim authResult = Await idClient.AcquireTokenByAuthorizationCode(scopes, notification.Code).ExecuteAsync()
Dim userDetails = Await Helpers.GraphHelper.GetUserDetailsAsync(authResult.AccessToken)
Dim cachedUser = New CachedUser() With {
.DisplayName = userDetails.DisplayName,
.Email = If(String.IsNullOrEmpty(userDetails.Mail), userDetails.UserPrincipalName, userDetails.Mail),
.Avatar = String.Empty,
.CompanyName = userDetails.CompanyName
}
tokenStore.SaveUserDetails(cachedUser)
Catch ex As MsalException
Dim message As String = "AcquireTokenByAuthorizationCodeAsync threw an exception"
notification.HandleResponse()
notification.Response.Redirect($"~/Views/ErrorPage?message={message}&debug={ex.Message}")
Catch ex As Microsoft.Graph.ServiceException
Dim message As String = "GetUserDetailsAsync threw an exception"
notification.HandleResponse()
notification.Response.Redirect($"~/Views/ErrorPage?message={message}&debug={ex.Message}")
End Try
End Function
User sign-in is initiated as follows:
Public Shared Sub SignIn()
System.Diagnostics.Debug.WriteLine("AccountController.vb SignIn()")
If Not HttpContext.Current.Request.IsAuthenticated Then
HttpContext.Current.Request.GetOwinContext().Authentication.Challenge(New AuthenticationProperties With {
.RedirectUri = "/"
}, OpenIdConnectAuthenticationDefaults.AuthenticationType)
End If
End Sub
I am not receiving any runtime error messages. There are no build errors or warnings. The app simply hangs once OWIN has taken care of the login process.
In a nutshell, I am trying to understand why program flow is not being passed from the GetOwinContext().Authentication.Challenge() method to the OnAuthorizationCodeReceivedAsync() Task. I have verified from the working MVC example this is the expected behavior.
EDIT:
After tracing both the MVC/C# and WebForms/VB.NET versions of the app, a side-by-side comparison of the two indicates that the WebForms version of the app hangs at the UseOpenIdConnectAuthentication() method. The associated OpenIdConnectAuthenticationNotifications were expanded to include all six available options.
From MVC/C# Startup.Auth.cs:
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = appId,
Scope = $"openid email profile offline_access {graphScopes}",
Authority = "https://login.microsoftonline.com/common/v2.0",
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailedAsync,
AuthorizationCodeReceived = OnAuthorizationCodeReceivedAsync,
RedirectToIdentityProvider = (context) =>
{
System.Diagnostics.Debug.WriteLine("*** RedirectToIdentityProvider");
return Task.FromResult(0);
},
MessageReceived = (context) =>
{
System.Diagnostics.Debug.WriteLine("*** MessageReceived");
return Task.FromResult(0);
},
SecurityTokenReceived = (context) =>
{
System.Diagnostics.Debug.WriteLine("*** SecurityTokenReceived");
return Task.FromResult(0);
},
SecurityTokenValidated = (context) =>
{
System.Diagnostics.Debug.WriteLine("*** SecurityTokenValidated");
return Task.FromResult(0);
}
}
}
);
The following notifications are received:
RedirectToIdentityProvider
MessageReceived
SecurityTokenReceived
SecurityTokenValidated
-- The OnAuthorizationCodeReceivedAsync() method is fired and an access token is retrieved and cached, as expected.
From WebForms/VB.NET Startup.Auth.vb:
app.UseOpenIdConnectAuthentication(New OpenIdConnectAuthenticationOptions With {
.ClientId = appId,
.Scope = $"openid email profile offline_access {graphScopes}",
.Authority = sAuthority,
.RedirectUri = redirectUri,
.PostLogoutRedirectUri = redirectUri,
.TokenValidationParameters = New TokenValidationParameters With {
.ValidateIssuer = False
},
.Notifications = New OpenIdConnectAuthenticationNotifications With {
.AuthenticationFailed = AddressOf OnAuthenticationFailedAsync,
.AuthorizationCodeReceived = AddressOf OnAuthorizationCodeReceivedAsync,
.RedirectToIdentityProvider = Function(context)
Debug.WriteLine("*** RedirectToIdentityProvider")
Return Task.FromResult(0)
End Function,
.MessageReceived = Function(context)
Debug.WriteLine("*** MessageReceived")
Return Task.FromResult(0)
End Function,
.SecurityTokenReceived = Function(context)
Debug.WriteLine("*** SecurityTokenReceived")
Return Task.FromResult(0)
End Function,
.SecurityTokenValidated = Function(context)
Debug.WriteLine("*** SecurityTokenValidated")
Return Task.FromResult(0)
End Function
}
})
The following notification is received:
- RedirectToIdentityProvider
-- The application hangs while waiting and no other events are fired.
I am trying to understand why the same OpenID Connect method results in such significantly different behavior between the MVC and WebForms versions of this app.

You are not specifying a response type, so I'm not sure the login is actually working. (Unless MSAL defaults the response_type to id_token, which is possible.)
You should be able to use the Configuration method from this quick start.
Regardless, OpenIdConnect does not typically use the Authorization Code flow for user login. So, the AuthorizationCodeReceived event would not happen.
Now, if your application wants to access a protected resource (Microsoft Graph, SharePoint, AAD-secured WebAPI) after login, then the AuthCode flow is appropriate. This would be the Web app that calls web APIs scenario.

Related

Getting wrong callback url for email confirmation in ASP NET Core 6

I am having very strange issue with my project. Everything works fine in debugging mode on localhost, but if I will publish it, I am getting the callback url without the https:// prefix. If user will click on the confirm button, some redirection tricky magic is happening as from url http://www.detailandgo.co.uk/Identity/Account/ConfirmEmail?userId=blablabla&code=foobar I am getting https://www.detailandgo.co.ukidentity/Account/ConfirmEmail?userId=blablabla&code=foobar.
As you can see, the url is correct, but I am missing the backslash between base url and "identity". If I will add it manually, everything works nicely. I was reading a lot about it, but haven't found any solution.
I am using ASP NET CORE 6 with Razor pages...
Thank you for all your suggestions and wishing you nice day!!
I've tried googleing a lot, having look in here at Stack Overflow, but haven't found any viable solution.
code which is generating the code:
_logger.LogInformation("User created a new account with password.");
_userManager.Options.SignIn.RequireConfirmedEmail = true;
var userId = await _userManager.GetUserIdAsync(user);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var test = Request.Scheme;
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
protocol: Request.Scheme);
string encodedCallBackUrl = HtmlEncoder.Default.Encode(callbackUrl);
Email email = new Email();
using (StreamReader reader = System.IO.File.OpenText(_webHostEnvironment.WebRootPath + "/Email/index.html"))
{
email.From = "info#detailandgo.co.uk";
email.Body = reader.ReadToEnd()
.Replace("{callbackUrl}", encodedCallBackUrl)
.Replace("{firstName}", Input.FirstName)
.Replace("{callbackBook}", Url.Page("/Index"));
email.IsHtml = true;
email.Subject = Input.FirstName + ", confirm your Detail&Go account";
email.To = Input.Email;
}
We can ensure the correctness of the URL through a certain format, as follows:
$"{Request.Scheme}://{Request.Host}/{encodedCallbackUrl.TrimStart('/')}";
I have researched this issue, and I think this should be the workaround or could be the soultion.
Change below below code
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
protocol: Request.Scheme);
string encodedCallBackUrl = HtmlEncoder.Default.Encode(callbackUrl);
to
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
protocol: Request.Scheme);
string encodedCallBackUrl = HtmlEncoder.Default.Encode(callbackUrl);
encodedCallBackUrl =$"{Request.Scheme}://{Request.Host}/{encodedCallbackUrl.TrimStart('/')}";

How to add Authentiction to WebApp with Pulumi

I am trying to add an authentication provider to a Pulumi WebApp but is totaly unclear to me how to achieve that. The class WebApp from package #pulumi/azure-native/web only offers the property identity but no property tho assign e.g. a Microsoft AD. Can anybody provide a hint on how to set this up?
There are some of the Pulumi Azure pre-requisites and have the appropriate permissions in your tenant and Azure subscription.
Follow the below steps to add the authentication to app service webapp with pulumi and deploy:
Creating the project:
Start by creating the application, and adding the AzureAD package we’ll need to create the Azure AD application registration.
pulumi new azure-csharp `
--name easyauth-webapp `
--description "azure ad secured app" `
--stack dev `
--config azure-native:location=eastus
dotnet add package Pulumi.AzureAD
We next need to update the contents of the pulumi.dev.yaml file to contain a few additional config items. Paste the following into the file:
config:
azure-native:location: eastus
azure-native:subscriptionId: UPDATE_ME
azure-native:tenantId: UPDATE_ME
easyauth-webapp:tenantId: UPDATE_ME
easyauth-webapp:ownerId: UPDATE_ME
easyauth-webapp:siteName: UPDATE_ME
easyauth-webapp:appRegistrationName: UPDATE_ME
You can set siteName and appRegistrationName to whatever you want.
The subscriptionId and tenantId should be set to the appropriate target’s for your Azure app service and Azure AD application registration, respectively.
The following commands may be helpful in retrieving these values:
# Get your user's id
az ad signed-in-user show --query objectId
# List all subscriptions (and their tenant) that you have access to
az account list
Deploy the website (with no security):
We’ll next create the website we want to deploy. We’re going to use the run from ZIP package functionality to deploy the contents of the wwwroot folder.
Create that folder and add some content to the index.htm file:
Ex:
<!-- wwwroot/index.htm -->
<html>
<head>
<title>A very secure app</title>
</head>
<body>
Hello EasyAuth with Pulumi!
</body>
</html>
Now we can deploy this file to Azure with Pulumi.
Modify the MyStack.cs file to contain the below code, which has been adapted from the Pulumi Function Stack example:
// MyStack.cs
using System;
using Pulumi;
using Pulumi.AzureAD;
using Pulumi.AzureAD.Inputs;
using Pulumi.AzureNative.Resources;
using Pulumi.AzureNative.Storage;
using Pulumi.AzureNative.Storage.Inputs;
using Pulumi.AzureNative.Web;
using Pulumi.AzureNative.Web.Inputs;
class MyStack : Stack
{
public MyStack()
{
var config = new Pulumi.Config();
var tenantId = config.Require("tenantId");
var ownerId = config.Require("ownerId");
var siteName = config.Require("siteName");
var appRegistrationName = config.Require("appRegistrationName");
var rg = new ResourceGroup($"RG-{siteName}");
var storageAccount = new StorageAccount("storageaccount", new StorageAccountArgs
{
ResourceGroupName = rg.Name,
Kind = "StorageV2",
Sku = new SkuArgs
{
Name = SkuName.Standard_LRS,
},
});
var appServicePlan = new AppServicePlan("appserviceplan", new AppServicePlanArgs
{
ResourceGroupName = rg.Name,
Kind = "App",
Sku = new SkuDescriptionArgs
{
Tier = "Basic",
Name = "B1",
},
});
var container = new BlobContainer("zips", new BlobContainerArgs
{
AccountName = storageAccount.Name,
PublicAccess = PublicAccess.None,
ResourceGroupName = rg.Name,
});
var blob = new Blob("appservice-blob", new BlobArgs
{
ResourceGroupName = rg.Name,
AccountName = storageAccount.Name,
ContainerName = container.Name,
Type = BlobType.Block,
Source = new FileArchive("wwwroot"),
});
var codeBlobUrl = SignedBlobReadUrl(blob, container, storageAccount, rg);
var app = new WebApp("app", new WebAppArgs
{
Name = siteName,
ResourceGroupName = rg.Name,
ServerFarmId = appServicePlan.Id,
SiteConfig = new SiteConfigArgs
{
AppSettings = {
new NameValuePairArgs{
Name = "WEBSITE_RUN_FROM_PACKAGE",
Value = codeBlobUrl,
}
},
}
});
this.Endpoint = app.DefaultHostName;
}
// From https://github.com/pulumi/examples/blob/master/azure-cs-functions/FunctionsStack.cs
private static Output<string> SignedBlobReadUrl(Blob blob, BlobContainer container, StorageAccount account, ResourceGroup resourceGroup)
{
return Output.Tuple<string, string, string, string>(
blob.Name, container.Name, account.Name, resourceGroup.Name).Apply(t =>
{
(string blobName, string containerName, string accountName, string resourceGroupName) = t;
var blobSAS = ListStorageAccountServiceSAS.InvokeAsync(new ListStorageAccountServiceSASArgs
{
AccountName = accountName,
Protocols = HttpProtocol.Https,
SharedAccessStartTime = "2021-01-01",
SharedAccessExpiryTime = "2030-01-01",
Resource = SignedResource.C,
ResourceGroupName = resourceGroupName,
Permissions = Permissions.R,
CanonicalizedResource = "/blob/" + accountName + "/" + containerName,
ContentType = "application/json",
CacheControl = "max-age=5",
ContentDisposition = "inline",
ContentEncoding = "deflate",
});
return Output.Format($"https://{accountName}.blob.core.windows.net/{containerName}/{blobName}?{blobSAS.Result.ServiceSasToken}");
});
}
[Output] public Output<string> Endpoint { get; set; }
}
We can now deploy the site and verify it has worked as intended:
pulumi up --stack dev
curl (pulumi stack --stack dev output Endpoint)
[
Securing the site:
To configure Easy Auth we first create an Azure AD application registration.
In this example I’m specifying AzureADMyOrg which restricts access to the tenant the application registration is deployed in. I’m also adding a RedirectUri that points at the Easy Auth middleware of the deployed site. A password is needed to use as a client secret (the web application being the client in this case).
Once the application registration is created we can add WebAppAuthSettings to our site. The example specifies no anonymous access (using RedirectToLoginPage), and connects the site to the application registration using the ClientId and ClientSecret (password).
Paste the below code just after the this.Endpoint... code in the above MyStack.cs:
// MyStack.cs
// After this.Endpoint = app.DefaultHostName;
var adApp = new Application("ADAppRegistration", new ApplicationArgs
{
DisplayName = appRegistrationName,
SignInAudience = "AzureADMyOrg",
Owners = new[] { ownerId },
Web = new ApplicationWebArgs
{
ImplicitGrant = new ApplicationWebImplicitGrantArgs
{
IdTokenIssuanceEnabled = true
},
RedirectUris = new System.Collections.Generic.List<string> { $"https://{siteName}.azurewebsites.net/.auth/login/aad/callback" }
}
}
);
var applicationPassword = new ApplicationPassword("appPassword", new ApplicationPasswordArgs
{
ApplicationObjectId = adApp.Id,
DisplayName = "Client secret for web app"
});
var allowedAudience = adApp.ApplicationId.Apply(id => $"api://{id}");
var authSettings = new WebAppAuthSettings("authSettings", new WebAppAuthSettingsArgs
{
ResourceGroupName = rg.Name,
Name = app.Name,
Enabled = true,
UnauthenticatedClientAction = UnauthenticatedClientAction.RedirectToLoginPage,
DefaultProvider = BuiltInAuthenticationProvider.AzureActiveDirectory,
ClientId = adApp.ApplicationId,
ClientSecret = applicationPassword.Value,
Issuer = $"https://sts.windows.net/{tenantId}/v2.0",
AllowedAudiences = new[] { allowedAudience },
});
We can now update the site, From the command line we can’t get much further than this.
But in a browser we’ll get redirected to complete the login flow and access the site.
pulumi up --stack dev
# Redirect to HTTPS
curl (pulumi stack --stack dev output Endpoint)
# Access denied
curl "https://$(pulumi stack --stack dev output Endpoint)"
Refer this Github link for pulumi samples.

create user accounts in office 365 using c#.net code dynamically

I want a sample application in c#.net which can create users in Office 365 using Microsoft API .
I wish to do it in code, not using Powershell.
You can use the Microsoft Graph API - Create User:
Register a Native Client App on Azure AD, assign the "Microsoft Graph" > "Read and Write Directory Data" permission.
string authority = "https://login.windows.net/yourdomain.onmicrosoft.com";
string clientId = "{app_client_id}";
Uri redirectUri = new Uri("http://localhost");
string resourceUrl = "https://graph.microsoft.com";
HttpClient client = new HttpClient();
AuthenticationContext authenticationContext = new AuthenticationContext(authority, false);
AuthenticationResult authenticationResult = authenticationContext.AcquireToken(resourceUrl,
clientId, redirectUri, PromptBehavior.Always);
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + authenticationResult.AccessToken);
string content = #"{
'accountEnabled': true,
'displayName': 'testuser',
'mailNickname': 'test',
'passwordProfile': {
'forceChangePasswordNextSignIn': true,
'password': 'pass#wd12345'
},
'userPrincipalName': 'testuser#yourdomain.onmicrosoft.com'
}";
var httpContent = new StringContent(content, Encoding.GetEncoding("utf-8"), "application/json");
var response = client.PostAsync("https://graph.microsoft.com/v1.0/users", httpContent).Result;
Console.WriteLine(response.Content.ReadAsStringAsync().Result);

Update claims after login with identityserver3 2.1.1

We need to update users claims after they log in to our website. This is caused by changes in the users licenses done by another part of our system.
However I am not able to comprehend how to update the claims without logout/login.
Rigth now this is our client setup
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
//user validation host
Authority = UrlConstants.BaseAddress,
//Client that the user is validating against
ClientId = guid,//if not convertet to Gui the compare from the server fails
RedirectUri = UrlConstants.RedirectUrl,
PostLogoutRedirectUri = UrlConstants.RedirectUrl,
ResponseType = "code id_token token",
Scope = "openid profile email roles licens umbraco_api umbracoaccess",
UseTokenLifetime = false,
SignInAsAuthenticationType = "Cookies",
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = async n =>
{
_logger.Info("ConfigureAuth", "Token valdidated");
var id = n.AuthenticationTicket.Identity;
var nid = new ClaimsIdentity(
id.AuthenticationType,
Constants.ClaimTypes.GivenName,
Constants.ClaimTypes.Role);
// get userinfo data
var uri = new Uri(n.Options.Authority + "/connect/userinfo");
var userInfoClient = new UserInfoClient(uri,n.ProtocolMessage.AccessToken);
var userInfo = await userInfoClient.GetAsync();
userInfo.Claims.ToList().ForEach(ui => nid.AddClaim(new Claim(ui.Item1, ui.Item2)));
var licens = id.FindAll(LicenseScope.Licens);
nid.AddClaims(licens);
// keep the id_token for logout
nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
n.AuthenticationTicket = new AuthenticationTicket(
nid,
n.AuthenticationTicket.Properties);
_logger.Info("ConfigureAuth", "AuthenticationTicket created");
},
RedirectToIdentityProvider = async n =>
{
// if signing out, add the id_token_hint
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
{
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token").Value;
_logger.Debug("ConfigureAuth", "id_token for logout set on request");
_logger.Debug("ConfigureAuth", "Old PostLogoutRedirectUri: {0}", n.ProtocolMessage.PostLogoutRedirectUri.ToString());
n.ProtocolMessage.IdTokenHint = idTokenHint;
var urlReferrer = HttpContext.Current.Request.UrlReferrer.ToString();
if (!urlReferrer.Contains("localhost"))
{
n.ProtocolMessage.PostLogoutRedirectUri = GetRedirectUrl();
}
else
{
n.ProtocolMessage.PostLogoutRedirectUri = urlReferrer;
}
_logger.Debug("ConfigureAuth", string.Format("Setting PostLogoutRedirectUri to: {0}", n.ProtocolMessage.PostLogoutRedirectUri.ToString()));
}
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.AuthenticationRequest)
{
n.ProtocolMessage.RedirectUri = GetRedirectUrl2();
n.ProtocolMessage.AcrValues = GetCurrentUmbracoId();
_logger.Debug("ConfigureAuth", string.Format("Setting RedirectUri to: {0}", n.ProtocolMessage.RedirectUri.ToString()));
}
},
}
});
We get our custom claims in SecurityTokenValidated
var licens = id.FindAll(LicenseScope.Licens);
nid.AddClaims(licens);
I do not follow how to get this without doing a login? Any help is highly appreciated.
That's a reminder that you should not put claims into tokens that might change during the lifetime of the session.
That said - you can set a new cookie at any point in time.
Reach into the OWIN authentication manager and call the SignIn method. Pass the claims identity that you want to serialize into the cookie.
e.g.
Request.GetOwinContext().Authentication.SignIn(newIdentity);

azure active directory graph REST api call through SP.WebRequestInfo on SharePoint Online

Trying to make a REST call through SharePoint's SP.WebRequestInfo.
I'm getting the error "The remote server returned the following error while establishing a connection - 'Unauthorized'." trying to call https://graph.windows.net/[Client]/users?api-version=2013-11-0.
I've successfully retrieved a access token.
Can you help me out why i'm getting this error?
Here is the code i'm using:
var url = "https://graph.windows.net/xxx/users/?api-version=2013-11-08";
var context = SP.ClientContext.get_current();
var request = new SP.WebRequestInfo();
request.set_url(url);
request.set_method("GET");
request.set_headers({
"Authorization": token.token_type + " " + token.access_token,
"Content-Type": "application/json"
});
var response = SP.WebProxy.invoke(context, request);
context.executeQueryAsync(successHandler, errorHandler);
function successHandler() {
if (response.get_statusCode() == 200) {
var responseBody = JSON.parse(response.get_body());
deferred.resolve(responseBody);
} else {
var httpCode = response.get_statusCode();
var httpText = response.get_body();
deferred.reject(httpCode + ": " + httpText);
}
}
The code for retrieving the token is:
this.getToken = function (clientId, clientSecret) {
var deferred = $q.defer();
var resource = "https://graph.windows.net";
var formData = "grant_type=client_credentials&resource=" + encodeURIComponent(resource) + "&client_id=" + encodeURIComponent(clientId) + "&client_secret=" + encodeURIComponent(clientSecret);
var url = "https://login.windows.net/xxxxxx.onmicrosoft.com/oauth2/token?api-version=1.0";
var context = SP.ClientContext.get_current();
var request = new SP.WebRequestInfo();
request.set_url(url);
request.set_method("POST");
request.set_body(formData);
var response = SP.WebProxy.invoke(context, request);
context.executeQueryAsync(successHandler, errorHandler);
function successHandler() {
if (response.get_statusCode() == 200) {
var token = JSON.parse(response.get_body());
deferred.resolve(token);
} else {
var httpCode = response.get_statusCode();
var httpText = response.get_body();
deferred.reject(httpCode + ": " + httpText);
}
}
function errorHandler() {
deferred.reject(response.get_body());
}
return deferred.promise;
};
Erik, something is strange here - you are using the client credential flow from a JavaScript client - this reveals the secret issued to the client app to the user of the JS app.
The client credential flow also requires the directory admin to grant directory read permission to the client application - not sure if this was already configured - nevertheless it must only be used with a confidential client, not a public client like a JS app.
Azure AD does not yet implement the implicit_grant oauth flow using which a JS client app can acquire an access token on behalf of the user over redirect binding (in the fragment). This is a hugh-pro requirement that we're working on - stay tuned.