Microsoft Teams API returning Unauthorized - vb.net

So I am working on setting up a new (private) bot for Microsoft Teams that should be able to post messages in a channel on-demand. I already have a bot coded for Google Hangouts Chat, but Microsoft Teams is giving me a really hard time.
I've been searching for over 10 hours now all over the web, and I am very confused.
Right now, all I want to do is post cards to a Microsoft Teams Channel. So I created the connectorclient, I used the baseuri provided when the bot joined the channel (Starts with smba.trafficmanager.net) with my MSAppID and MSAppPassword. Then, I fill in as much information as I can (Maybe too much?) and I submit the information using the connector's .conversations.createconversation.
Namespaces used: Microsoft.bot.connector, Microsoft.bot.connector.teams.models
Here's the code:
Dim Connector As New ConnectorClient(New Uri("https://smba.trafficmanager.net/amer/"), "MSAPPID", "MSAPPPASSWORD")
Dim conversation As New ConversationParameters
Dim activity2 = Activity.CreateMessageActivity
Dim bot As New ChannelAccount
bot.Id = "BOTID"
bot.Name = "EDD Bot Test"
conversation.Bot = bot
Dim chaninfo As New ChannelInfo
chaninfo.Id = "CHANID"
chaninfo.Name = "General"
Dim teaminfo As New TeamInfo
teaminfo.Id = "TEAMID"
teaminfo.Name = "EDD"
activity2.Text = "Test"
activity2.ServiceUrl = "https://smba.trafficmanager.net/amer/"
activity2.Type = ActivityTypes.Message
activity2.From = bot
activity2.ChannelId = "msteams"
Dim tenantdata As New TenantInfo
tenantdata.Id = "TENANTID"
Dim teamschanneldata As New TeamsChannelData
teamschanneldata.Channel = chaninfo
teamschanneldata.Team = teaminfo
teamschanneldata.Tenant = tenantdata
activity2.ChannelData = teamschanneldata
conversation.Activity = activity2
Response.Write(JsonConvert.SerializeObject(conversation))
Try
Dim reqresp As ConversationResourceResponse = Connector.Conversations.CreateConversation(conversation)
Response.Write("ActivityID: " & reqresp.ActivityId & ", ServiceURL: " & reqresp.ServiceUrl & ", ID: " & reqresp.ServiceUrl)
Catch ex As ErrorResponseException
Response.Write(ex.Response.Content & " " & ex.Response.ReasonPhrase)
End Try
This is what the API returns:
{"message":"Authorization has been denied for this request."} Unauthorized
Keep in mind, I'm not even 100% sure I'm using the right method to create the channel message, I figured it was either CreateConversation or ReplyToActivity.
I gave the app Users.ReadWriteAll permissions too, so am I missing something? That error leads me to think it doesn't have anything to do with the ConversationParameters payload but something to do with authentication.

Problem was solved by using MicrosoftAppCredentials.TrustserviceUrl for both the serviceUrl and the endpoint message.

Related

Microsoft Graph SendMail returns ErrorInternalServerError

I'm writing a program to send internal emails in VB.Net using the official Microsoft Graph package. At first I got it to send calendar invites and that worked fine for months, but when I tried to add the ability to send emails I keep getting an Internal Server Error.
From looking at other times people have got this error from Graph, it appears to be when a malformed request is sent or when values that don't quite make sense are given. I'm using the constructors from the package and using basically the same construction method as every example online, so the structure of the request can't be wrong, and the only fields that I'm using just take text and don't do anything with it, so I don't think that can be the issue either.
I have the correct permissions in Azure: Screenshot of permissions in Azure, including Mail.Send
Here's my code for constructing the email:
Function DefineImportantEmail(Locations As String, Recipients As List(Of (String, String))) As Message
Dim title = "IMPORTANT OUTAGE NOTIFICATION"
Dim message = "Outage at " & Locations
Dim RecipientList = New List(Of Recipient)
For Each recipient In Recipients
RecipientList.Add(New Attendee With {
.EmailAddress = New EmailAddress With {
.Address = recipient.Item1,
.Name = recipient.Item2
},
.Type = AttendeeType.Optional
})
Next
Return New Message With {
.Subject = title,
.Body = New ItemBody With {
.ContentType = BodyType.Text,
.Content = message
},
.ToRecipients = RecipientList
}
End Function
And here's the code I use to send the email:
Sub SendImportantEmail(Locations As String, Recipients As List(Of (String, String)))
Dim SaveToSentItems = True
Dim graphClient = GetGraphClient()
Dim EventObject = DefineImportantEmail(Locations, Recipients)
Dim request = graphClient.Users("xxxxx#xxxxxxxxxxxxxxx.com").SendMail(EventObject, SaveToSentItems).Request().PostAsync()
request.Wait()
End Sub
The GetGraphClient function works for any other API endpoint I've tried, so I haven't included it. The censored user is from our organisation and is also the user I sent calendar invites from in the first version of this program.
Here's the full error message I get:
(Code: ErrorInternalServerError
Message: An internal server error occurred. The operation failed.
ClientRequestId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
)
---> Status Code: InternalServerError
Microsoft.Graph.ServiceException: Code: ErrorInternalServerError
Message: An internal server error occurred. The operation failed.
ClientRequestId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

.NET Google API access token failing with no refresh token specified

I am trying to set up a class that can wrap around the .NET Google API so that I can use an Access Token that I have previously obtained to access a user's Google Drive. As of right now, I am just trying to get it to work so that I do not require a Refresh Token (more on that in a second). The ultimate goal is for somebody to go through a web page I have set up to authenticate where I obtain both an Access Token and a Refresh Token by directly calling to the Google Rest API (which I store in a database). They can then request to upload/download files onto their Drive on a different page which will first obtain the appropriate information from the database and then use the .NET Google API Library when accessing Drive.
However, when I attempt to access their Drive I get the the following error:
The access token has expired and could not be refreshed. Errors: refresh error, refresh error, refresh error
I know that the Access Token is valid because I obtain it only seconds earlier during my testing. Here is my code for setting up the Drive Service:
' NOTE: Code altered for brevity
Public Sub Initialize(accessToken As String)
' Set up the client secret information based on the default constants
Dim clientSecrets As New ClientSecrets()
clientSecrets.ClientId = DEFAULT_CLIENT_ID
clientSecrets.ClientSecret = DEFAULT_CLIENT_SECRET
' Set up a token based on the token data we got
' NOTE: Is it OK to leave some strings as NULL?
Dim token As New Responses.TokenResponse()
token.AccessToken = accessToken
token.RefreshToken = ""
token.TokenType = "Bearer"
token.IssuedUtc = DateTime.Now
token.ExpiresInSeconds = 3600
token.Scope = "drive"
token.IdToken = ""
' Set up a flow for the user credential
Dim init As New GoogleAuthorizationCodeFlow.Initializer()
init.ClientSecrets = clientSecrets
init.Scopes = New String() {DriveService.Scope.Drive}
init.Clock = Google.Apis.Util.SystemClock.Default
' Set up everything else and initialize the service
Dim baseInit As New BaseClientService.Initializer()
baseInit.HttpClientInitializer = New UserCredential(New GoogleAuthorizationCodeFlow(init), "user", token)
baseInit.ApplicationName = APP_NAME
_service = New DriveService(baseInit)
End Sub
Shortly after that, I then use the following code to request a folder so I can check to see if it exists or not.
Private Function GetDriveFolder(folderPath As String, ByRef folderIds As String(), Optional createMissingFolders As Boolean = False, Optional parentFolderId As String = "root") As Data.File
Dim creatingFolderPath As Boolean = False
Dim currentFolder As Data.File = Nothing
Dim folderPathSplit As String() = folderPath.Replace("/", "\").Trim("\").Split("\")
Dim folderIdList As New List(Of String)
folderIds = {}
' Loop through each folder in the path and seek each out until we reach the end
For x As Integer = 0 To folderPathSplit.Length - 1
Dim result As FileList = Nothing
If Not creatingFolderPath Then
' Build a list request which we will use to seek out the next folder
Dim request As FilesResource.ListRequest = _service.Files.List()
request.Q = "mimeType='application/vnd.google-apps.folder' and name='" & folderPathSplit(x) & "'"
If currentFolder Is Nothing Then
request.Q &= " and '" & EscapeDriveValue(parentFolderId) & "' in parents"
Else
request.Q &= " and '" & EscapeDriveValue(currentFolder.Id) & "' in parents"
End If
request.Spaces = "drive"
request.Fields = "files(id, name)"
' Execute the search, we should only get a single item back
' NOTE: Error thrown on this request
result = request.Execute()
End If
' So on.....
So, I'm just trying to get it to work with only the Access Token for the time being because if it ends up getting refreshed I'll need to know so that I can update my database. However, if I do include the Refresh Token I get the following error:
Error:"unauthorized_client", Description:"Unauthorized", Uri:""
I'm guessing this has something to do with the way I have configured my application through the Dev Console but if I authenticate through the Google API Library by having it launch a browser to get my credentials everything works fine. So, I'm really not sure where to go from here as I haven't found anybody having similar problems and the guides don't cover specifying your own Access Token.
Also, as a quick note this is the URL I am using when having the user authenticate:
String.Format("https://accounts.google.com/o/oauth2/v2/auth?client_id={0}&state={1}&redirect_uri={2}&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive&access_type=offline&include_granted_scopes=true&prompt=select_account%20consent&response_type=code", GOOGLEAPI_CLIENTID, validateState, redirectUri)
Thanks for the help!
If you have an access-token then the simplest way to create a google credential is to use the GoogleCredential.FromAccessToken() method passing in your access token.
This returns you a GoogleCredential instance which you can use to set the HttpClientInitializer property when building the DriveService.
If you then still get an error when accessing the drive service, then it's likely there's something incorrect in how you are asking for the access-token.

VBA Excel: Retrieve/display email using Gmail API on excel

I research regarding retrieving or displaying emails from gmail without using outlook and it led me here on using Gmail API. I'm confused because I don't have idea on how to solve my problem.
Here's the code:
Attribute VB_Name = "Gmail"
' Setup client and authenticator (cached between requests)
Private pGmailClient As WebClient
Private Property Get GmailClient() As WebClient
If pGmailClient Is Nothing Then
' Create client with base url that is appended to all requests
Set pGmailClient = New WebClient
pGmailClient.BaseUrl = "https://www.googleapis.com/gmail/v1/"
' Use the pre-made GoogleAuthenticator found in authenticators/ folder
' - Automatically uses Google's OAuth approach including login screen
' - Get API client id and secret from https://console.developers.google.com/
' - https://github.com/timhall/Excel-REST/wiki/Google-APIs for more info
Dim Auth As New GoogleAuthenticator
Auth.Setup CStr(Credentials.Values("Google")("id")), CStr(Credentials.Values("Google")("secret"))
Auth.AddScope "https://www.googleapis.com/auth/gmail.readonly"
Auth.Login
Set pGmailClient.Authenticator = Auth
End If
Set GmailClient = pGmailClient
End Property
' Load messages for inbox
Function LoadInbox() As Collection
Set LoadInbox = New Collection
' Create inbox request with userId and querystring for inbox label
Dim Request As New WebRequest
Request.Resource = "users/{userId}/messages"
Request.AddUrlSegment "userId", "me"
Request.AddQuerystringParam "q", "label:inbox"
Dim Response As WebResponse
Set Response = GmailClient.Execute(Request)
If Response.StatusCode = WebStatusCode.Ok Then
Dim MessageInfo As Dictionary
Dim Message As Dictionary
For Each MessageInfo In Response.Data("messages")
' Load full messages for each id
Set Message = LoadMessage(MessageInfo("id"))
If Not Message Is Nothing Then
LoadInbox.Add Message
End If
Next MessageInfo
End If
End Function
' Load message details
Function LoadMessage(MessageId As String) As Dictionary
Dim Request As New WebRequest
Request.Resource = "users/{userId}/messages/{messageId}"
Request.AddUrlSegment "userId", "me"
Request.AddUrlSegment "messageId", MessageId
Dim Response As WebResponse
Set Response = GmailClient.Execute(Request)
If Response.StatusCode = WebStatusCode.Ok Then
Set LoadMessage = New Dictionary
' Pull out relevant parts of message (from, to, and subject from headers)
LoadMessage.Add "snippet", Response.Data("snippet")
Dim Header As Dictionary
For Each Header In Response.Data("payload")("headers")
Select Case Header("name")
Case "From"
LoadMessage.Add "from", Header("value")
Case "To"
LoadMessage.Add "to", Header("value")
Case "Subject"
LoadMessage.Add "subject", Header("value")
End Select
Next Header
End If
End Function
Sub Test()
Dim Message As Dictionary
For Each Message In LoadInbox
Debug.Print "From: " & Message("from") & ", Subject: " & Message("subject")
Debug.Print Message("snippet") & vbNewLine
Next Message
End Sub
I'm using the blank file from this GitHub Link that enables this code above. I was stuck on this error:
Still I don't have the idea what's the output of this API because it's my first time using this one. I have my id and secret already from Gmail API i just removed it from the code. I guess it was really hard if I don't have someone I can do brainstorming regarding on this matter.
Hoping someone have encountered this problem or anyone is interested. Thanks.

Unable to Send Mail (VB.net)

I am trying to send a mail using Dart Control. It was working well with a particular server, but since I switched to a secured server, I receive the following error message:
Protocol Exception--
Request: AUTH LOGIN
Response: 530 Must issue a STARTTLS command first
Below is the code :
Dim Smtp1 As Smtp = New Smtp
Dim SMTPResult As SmtpResult
Dim Message As Dart.Mail.MailMessage = New Dart.Mail.MailMessage()
Smtp1.Session.RemoteEndPoint.Port = intPortNo
strErrLoc = "SMTP1.DnsServerTimeout"
Smtp1.DnsServerTimeout = 15 'default time out 30 seconds
strErrLoc = "Set User Name"
Smtp1.Session.Username = strUserID
Smtp1.Session.Password = strPWD
strErrLoc = "Subject and Mail TEXT"
Message.Subject = strSubject
If strHTMLEmail.Trim = "" Then
Message.Text = strMailText
Else
Message.Html = strHTMLEmail
End If
Smtp1.Session.Authentication = Authentication.Auto
Smtp1.Session.RemoteEndPoint.HostNameOrAddress = strServerName.Trim
Smtp1.Session.ServicePrincipleName = "SMTP/" & strServerName.Trim
SMTPResult = Smtp1.Send(Message)
try with enabling SSL mode,like
Smtp1.EnableSSL = True
this works in case of Gmail ,Please Check with yours.
The suggestion is to use Explicit security.
The following Code implementing Explicit Security resolved the issue.
Smtp1.Session.Security.Encrypt = Encrypt.Explicit

Posting tweets from vb.net

How could I post custom tweets on my twitter account using vb.net?
If is not possible, which I doubt, is there any way to achive this with c# or javascript?
You can use the twitterizer library.
Here's an example how to use it:
Dim tokens As New OAuthTokens()
tokens.AccessToken = "XXX"
tokens.AccessTokenSecret = "XXX"
tokens.ConsumerKey = "XXX"
tokens.ConsumerSecret = "XXX"
Dim tweetResponse As TwitterResponse(Of TwitterStatus) = TwitterStatus.Update(tokens, "Hello, #Twitterizer")
If tweetResponse.Result = RequestResult.Success Then
' Tweet posted successfully!
Else
' Something bad happened
End If
BTW, here's a list of avaiable twitter libraries (but it's not 100% up-to-date).
You can do it in LINQ to Twitter like this:
Dim credentials As IOAuthCredentials = New InMemoryCredentials
credentials.ConsumerKey = "xxx"
credentials.ConsumerSecret = "xxx"
credentials.OAuthToken = "xxx"
credentials.AccessToken = "xxx"
Dim auth As PinAuthorizer = New PinAuthorizer()
auth.Credentials = credentials
Dim twitterCtx As TwitterContext = New TwitterContext(auth)
twitterCtx.UpdateStatus(statusMsg)
and if you want to perform a query, you can do it like this:
Dim queryResults = _
From search In twitterCtx.Search _
Where search.Type = SearchType.Search _
And search.Query = "LINQ to Twitter"
More info here: http://linqtotwitter.codeplex.com/.
A super easy way of doing this is by using IFTTT and my IFTTT Maker.net libary
First, in IFTTT create a new recipe that's triggered by the Maker channel and name the event "tweet_now".
Then, select the Twitter engine and click "Post a tweet", remove everything from the input box and replace it with {{value1}} and click create recipe.
After that, in Visual studio, add ifttt.vb to your project. Now for the code:
Try
makechannel.scode = "your account ID"
makechannel.fireevent("tweet_now", "Tweet text", "", "")
'code goes here if done
Catch ex As Exception
'code goes here if it fails
End Try
Then fill in your account ID. You can find it at ifttt.com/maker
And that's it!