Microsoft Graph API Forbiden - asp.net

Imports Microsoft.Graph
Imports Azure.Identity
Module Module1
Sub Main()
Dim onlineMeeting = New OnlineMeeting With {
.StartDateTime = DateTimeOffset.Parse("2022-04-23T21:33:30.8546353+00:00"),
.EndDateTime = DateTimeOffset.Parse("2022-04-23T22:03:30.8566356+00:00"),
.Subject = "Application Token Meeting",
.Participants = New MeetingParticipants With {
.Organizer = New MeetingParticipantInfo With {
.Identity = New IdentitySet With {
.User = New Identity With {
.Id = "ba525532-764a-4aa1-8103-066beca0f5a8"
}
}
}
}
}
Dim testing As Task(Of OnlineMeeting) = createMeeting(onlineMeeting)
Console.WriteLine(testing.Status.ToString)
Console.ReadKey()
End Sub
Public Async Function createMeeting(onlineMeeting As OnlineMeeting) As Task(Of OnlineMeeting)
Dim createMeetings As OnlineMeeting = Nothing
Try
Dim clientId As String = "XXXXXXXXX"
Dim clientSecret As String = "XXXXXXXXXXXXX"
Dim tenantId As String = "XXXXXXXXXXX"
Dim options = New TokenCredentialOptions With
{
.AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
}
Dim ClientSecretCredential = New ClientSecretCredential(tenantId, clientId, clientSecret, options)
Dim graphClient As GraphServiceClient = New GraphServiceClient(ClientSecretCredential)
createMeetings = Await graphClient.Communications.OnlineMeetings.Request().AddAsync(onlineMeeting)
Catch ex As ServiceException
Console.WriteLine(ex.RawResponseBody)
End Try
Return createMeetings
End Function
End Module
When i execute this i get :
WaitingForActivation
{"error":{"code":"Forbidden","message":"","innerError":{"request-id":"XXXXX","date":"2021-08-27T11:38:30","client-request-id":"XXXXXXXX"}}}
Can someone help me?

This normally means there is no permission to perform the action.
Check you are including the relavent scope when authenticating against graph. In this case it is 'OnlineMeetings.ReadWrite' for delegated permissions and 'OnlineMeetings.ReadWrite.All' for application permissions
If using delegated permissions check the delegated user has permission to perform this action, if they can't do it manually you can't do it through graph.
If using application permissions make sure you have created an appropriate application access policy as described in the blue note here: https://learn.microsoft.com/en-us/graph/api/application-post-onlinemeetings?view=graph-rest-1.0&tabs=http
Administrators must create an application access policy and grant it to a user, authorizing the app configured in the policy to create an online meeting on behalf of that user (user ID specified in the request path).

Related

Asp.net UseOpenIdConnectAuthentication not working in Azure

I am using UseOpenIdConnectAuthentication to authenticate users. My application code works fine locally. But, when I run it on Azure, the SecurityTokenValidated event is never fired. Consequently, the code runs fine but the user is never authenticated. I am not sure if the issue is with my code or with Azure. This is being used in a Web Form, Asp.net application (not Core). I use the Azure trace feature to log. I can see that only "RedirectToIdentityProvider" is fired. No other event gets called. Here is my code:
Startup.Auth.Vb:
Public Sub ConfigureAuth(app As IAppBuilder)
Dim clientId As String = ""
Dim authority As String = ""
Dim redirectURI As String
Trace.TraceInformation("Hit Config Auth function")
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap = New Dictionary(Of String, String)
app.SetDefaultSignInAsAuthenticationType("Cookies")
app.UseCookieAuthentication(New CookieAuthenticationOptions() With {
.AuthenticationMode = AuthenticationMode.Active,
.CookieManager = New SystemWebCookieManager
})
redirectURI = appSettings("ID_Redirect_URI")
clientId = appSettings("ID_ClientID")
authority = appSettings("ID_Authority")
Trace.TraceInformation(redirectURI)
Trace.TraceInformation(clientId)
Trace.TraceInformation(authority)
Trace.TraceInformation("creating OpenIDAuthOptions")
Dim OpenIdAuthOption = New OpenIdConnectAuthenticationOptions() With {
.SignInAsAuthenticationType = "Cookies",
.Authority = authority,
.RequireHttpsMetadata = False,
.ClientId = clientId,
.ResponseType = "id_token",
.Scope = "openid profile roles",
.RedirectUri = redirectURI,
.PostLogoutRedirectUri = redirectURI,
.Notifications = New OpenIdConnectAuthenticationNotifications() With {
.AuthenticationFailed = Function(ctx)
Trace.TraceInformation("Auth Failed event")
Return Task.FromResult(0)
End Function,
.SecurityTokenReceived = Function(ctx)
Trace.TraceInformation("Sec Token Recieved event")
Return Task.FromResult(0)
End Function,
.MessageReceived = Function(ctx)
Trace.TraceInformation("Message Recieved event")
Return Task.FromResult(0)
End Function,
.SecurityTokenValidated = Function(ctx)
Trace.TraceInformation("Security token validated")
Return Task.FromResult(0)
End Function,
.AuthorizationCodeReceived = Function(ctx)
Trace.TraceInformation("Auth Code Recieved event")
Return Task.FromResult(0)
End Function,
.RedirectToIdentityProvider = Function(context)
Trace.TraceInformation("start of RedirectToIDProvider")
Return Task.FromResult(0)
End Function
}
}
Trace.TraceInformation("adding OpenIdAuthOptyions")
app.UseOpenIdConnectAuthentication(OpenIdAuthOption)
Trace.TraceInformation("finihsed adding OpenIdAuthOptyions")
End Sub
As I mentioned above, this code works fine locally. It only does not work when hosted on Azure. When running locally, the events are fired in this order:
RedirectToIdentityProvider
Message Received
Security Token Received
Security Token Validated
But, in Azure, only RedirectToIdentityProvider is fired.
Changed your Action to take when request is not authenticated in App Service Authentication/Authorization section in the azure portal from LogIn with Azure Active Directory to Allow Anonymous requests. As shown on the picture below:
Then the SecurityTokenValidated would be fired. App services auth takes place outside of you app, so customized auth code in your app never gets a chance to run. When you turn that off it allows your app to handle the auth itself the same way it does locally.
Here is the similar issue you could refer to.
Try changing the application manifest of the application definition on Azure to set the "oauth2AllowIdTokenImplicitFlow" property to true from false.
Go to the Azure Portal,
Select to Azure Active Directory
Select App Registrations
Select your app.
Click on Manifest
Find the value oauth2AllowIdTokenImplicitFlow and change it's value to true
Click Save
2) In your startup.cs file, change the following:
ResponseType = OpenIdConnectResponseType.Code
to
ResponseType = OpenIdConnectResponseType.CodeIdToken
and see if it helps.

ASP.net Identity management with Azure, in Web Form

I have a Webform ASP.net (in VB.net) that I want to add identity management too. I have added code that allows for the registration of users. But, I am stuck on how to log a user in. My application uses SSO with Azure Active Directory. What I would like to do is, after the user Authenticates with Azure, then use Identity management to see if the user is in the web system and if they have roles.
This site can be accessed by three types of users: 1) Internal folks who would have a local account as well as an Azure AD account, who act as admins for the site 2) Internal folks who authenticate with Azure AD, but will not have local accounts (i.e authenticated but with no roles) 3) External folks who will not authenticate with Azure AD and will not have local accounts
My questions is, how do I use Identity management, after the users authenticates with Azure AD? Here is my code:
Startup.Auth.vb:
Partial Public Class Startup
Dim appSettings = ConfigurationManager.AppSettings
Private realm As String
Private aadInstance As String
Private tenant As String
Private metadata As String
Private authority As String
Public Sub ConfigureAuth(app As IAppBuilder)
Dim appSettings = ConfigurationManager.AppSettings
realm = ConfigurationManager.AppSettings("ida:RPIdentifier")
aadInstance = ConfigurationManager.AppSettings("ida:AADInstance")
tenant = ConfigurationManager.AppSettings("ida:Tenant")
metadata = String.Format("{0}/FederationMetadata/2007-06/FederationMetadata.xml", aadInstance)
authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant)
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType)
app.UseCookieAuthentication(New CookieAuthenticationOptions())
Dim authOption As WsFederationAuthenticationOptions = New WsFederationAuthenticationOptions()
Dim fn = Function(context)
context.HandleResponse()
context.Response.Redirect("Home/Error?message=" + context.Exception.Message)
Return Task.FromResult(0)
End Function
Dim auth_not As WsFederationAuthenticationNotifications = New WsFederationAuthenticationNotifications() With {
.AuthenticationFailed = fn
}
Dim auth_opt As WsFederationAuthenticationOptions = New WsFederationAuthenticationOptions() With {
.Wtrealm = realm,
.MetadataAddress = metadata,
.Notifications = auth_not
}
app.UseWsFederationAuthentication(auth_opt)
End Sub
End Class
Default.aspx.vb login click:
Public Sub SignIn()
Dim newAuth As AuthenticationProperties = New AuthenticationProperties()
newAuth.RedirectUri = "/"
HttpContext.Current.GetOwinContext().Authentication.Challenge(newAuth, WsFederationAuthenticationDefaults.AuthenticationType)
End Sub
The problem is, I don't know where to user the UserManager to get the logged in user, after the user gets redirected to our companies log in page, and they are redirected back to the site, with the returned Azure AD Username.

WEB API + Cache all users from AD on application_start

I have a WEB API which perform 2 functions
return current login user details
Also enable asyn search for any user.
In order to save performance hit on first call, i want to get all users and save them in cache on application_start. I have enabled IIS warm up, so everything should be in cache before first request
DirectoryEntry entry = new DirectoryEntry(LdapPath, activeDirectoryUserid, activeDirectoryPassword);
DirectorySearcher search = new DirectorySearcher(entry)
{
Filter = "(&(&(objectClass = user)(objectClass = person)))"
};
search.PropertiesToLoad.Add(AppConstants.AD.Name);
search.PropertiesToLoad.Add(AppConstants.AD.SamAccountName);
search.PropertiesToLoad.Add(AppConstants.AD.ThumbnailPhoto);
search.PropertiesToLoad.Add(AppConstants.AD.FirstName);
search.PropertiesToLoad.Add(AppConstants.AD.Manager);
search.PropertiesToLoad.Add(AppConstants.AD.DistinguishedName);
search.CacheResults = false;
SearchResultCollection resultset = search.FindAll();
Try using System.DirectoryServices.AccountManagement namespace.
using(PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "yourdomain.com"))
{
UserPrincipal allUsers = new UserPrincipal(ctx);
PrincipalSearcher ps = new PrincipalSearcher(allUsers);
foreach (UserPrincipal user in ps.FindAll())
{
//USER will contain the necessary information you need
string name = user.DisplayName;
}
}

Asp.Net application with DropBox not working

I set the DropBox application type Full DropBox; I set on my application the following function:
Private Const AppKey As String = "my key"
Private Const AppSecret As String = "my secret"
Private Function Upload() As String
Dim client As DropNetClient
Dim token As UserLogin
Dim userToken As String = My.Settings.userToken
Dim userSecret As String = My.Settings.userSecret
Dim needAccessToken As Boolean = (String.IsNullOrEmpty(userToken) Or String.IsNullOrEmpty(userSecret))
If (needAccessToken) Then
client = New DropNet.DropNetClient(AppKey, AppSecret)
client.UseSandbox = True
client.GetToken()
Dim url = client.BuildAuthorizeUrl()
Try
token = client.GetAccessToken()
Catch ex As Exception
Console.WriteLine("Exception! " + ex.Message)
Exit Function
End Try
userToken = token.Token
userSecret = token.Secret
My.Settings.Properties.Item("userToken").DefaultValue = userToken
My.Settings.Properties.Item("userSecret").DefaultValue = userSecret
My.Settings.Save()
Else
client = New DropNet.DropNetClient(AppKey, AppSecret, userToken, userSecret)
client.UseSandbox = True
End If
Dim rawData As Byte() = File.ReadAllBytes(Server.MapPath("") + "/Fax/" + "Fax.zip")
Dim result As MetaData = client.UploadFile("/", "fax.zip", rawData)
End Function
Unfortunately education tokens = GetAccessToken () I get the error:
Received Response [Unauthorized]: Expected to see [OK]. The HTTP response was [{" "error" ":" "Request token has not been properly authorized by a user. ""}] ".
I checked the URL (client.BuildAuthorizedUrl ()) and returns me "Unauthorized".
How can I link to Dropbox folder? I do something wrong? Or do I need to set better application of Dropbox?
Once you get the URL via BuildAuthorizeUrl, you need to send the user to that address and have them authorize your app. Only once they've done that can you call GetAccessToken.
The error you're seeing is because you're trying to get the access token before the user has actually authorized your app.

Incremental Google OAuth with Asp.Net Owin Oauth

I'm looking for a solution to doing incremental authorization against Google's api's with Asp.Net's Owin OAuth Libraries.
I know how to set scope for specific api's, but I would like to do it incrementally and can only see how to set it on globally.
Doc on Google Oauth Incremental Auth...
https://developers.google.com/accounts/docs/OAuth2WebServer#incrementalAuth
Current VB Code...
Public Sub ConfigureAuth(app As IAppBuilder)
Dim googleCreds = New GoogleOAuth2AuthenticationOptions() With {
.ClientId = "xxxx",
.ClientSecret = "xxx"
}
googleCreds.Scope.Add("https://www.googleapis.com/auth/analytics.readonly")
app.UseGoogleAuthentication(googleCreds)
' Would like to add another way to specify GoogleDrive, YouTube, Google+ scopes
' Example code that doesn't work that would add a 2nd Google Oauth Listener
googleCreds.Scope.Clear()
googleCreds.Scope.Add("https://www.googleapis.com/auth/drive.file")
googleCreds.AuthenticationType = "GoogleDrive"
app.UseGoogleAuthentication(googleCreds)
End Class
Here is the solution I came up with. It involves passing a "scope" parameter in the url and then parsing that in the "OnApplyRedirect" function of the Authentication options and then manually injecting the correct scope url into the redirect url.
Dim googleCreds = New GoogleOAuth2AuthenticationOptions() With {
.ClientId = "xxx",
.ClientSecret = "xxx",
.Provider = New Microsoft.Owin.Security.Google.GoogleOAuth2AuthenticationProvider() With { _
.OnApplyRedirect = Function(context)
Dim queryString = HttpContext.Current.Request.QueryString.ToString()
Dim queryParms = HttpUtility.ParseQueryString(queryString)
' Change the value of "redirect" here
' e.g. append access_type=offline
Dim redirect As String = context.RedirectUri
redirect += "&access_type=offline"
redirect += "&approval_prompt=force"
redirect += "&include_granted_scopes=true"
Dim uri = New Uri(redirect)
If (Not String.IsNullOrEmpty(queryParms.Get("scope"))) Then
Dim scope = queryParms.Get("scope")
Dim redirectQueryString = HttpUtility.ParseQueryString(uri.Query)
Select Case scope
Case "Analytics"
redirectQueryString.Set("scope", "https://www.googleapis.com/auth/analytics.readonly")
Case "YoutTube"
redirectQueryString.Set("scope", "https://gdata.youtube.com")
Case "Drive"
redirectQueryString.Set("scope", "https://www.googleapis.com/auth/drive.file")
Case Else
LoggingUtility.LogErrorMessage("Invalid scope passed in: scope: " + scope)
End Select
redirect = uri.GetLeftPart(UriPartial.Path) + "?" + redirectQueryString.ToString()
End If
context.Response.Redirect(redirect)
End Function, _
}
}
'Google Analytics
app.UseGoogleAuthentication(googleCreds)
In OWIN version 3.1 incremental authentication works as expected. (It may well work in earlier versions, I've not tested that).
There were two tricks that were necessary to get this working:
OWIN expects a 401
If you try and call:
var ctx = HttpContext.Current.Request.GetOwinContext();
var properties = new AuthenticationProperties
{
RedirectUri = "/your/redirect",
};
ctx.Authentication.Challenge(properties, "Google");
While logged in to your application, the call to Google from the Middleware will not happen.
This can be confusing, since it just works, when using the exact same code during login.
The reason is that the Middleware only works when the current HTTP response is 401, so after the call above you need something like:
Page.Response.StatusCode = 401;
Page.Response.End();
Specification of scopes is picky
In my original code to try and call with a new scope, I had something like this:
var ctx = HttpContext.Current.Request.GetOwinContext();
var properties = new AuthenticationProperties
{
RedirectUri = redirect,
};
properties.Dictionary["scope"] ="https://www.googleapis.com/auth/calendar.readonly";
This was calling Google as expected and Goggle was correctly challenging based on the requested scopes, but then the response back to my server was breaking (never did find out where).
What I found I needed to do was to resend all my base scopes as well:
var scopes = new List<string>
{
"https://www.googleapis.com/auth/plus.me",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile",
};
if (additionalScopes != null)
{
scopes = scopes.Union(additionalScopes).ToList();
}
properties.Dictionary["scope"] = string.Join(" ", scopes);

Resources