Microsoft Graph / OneDrive requesting multiple authentications - vb.net

I'm trying to write code to read files in a OneDrive folder of mine and, in the future, create, move and delete them.
The scrap of code below works, but it requests a new authentication (shows Microsoft's OAuth window) for each iteration in the For Each ... Next.
What am I doing wrong here?
Imports Microsoft.Graph
Public Class FormGraphClient
Private Shared client As GraphServiceClient
Private Async Sub FormGraphClient_Load(sender As Object, e As EventArgs) Handles MyBase.Load
client = AuthenticationHelper.GetAuthenticatedClient
Dim formsmonitor_items = Await client.Me.Drive.Root.ItemWithPath("FormsMonitor").Children.Request.GetAsync
Dim forms As New Dictionary(Of String, String)
For Each form In formsmonitor_items
If form.Name Like "*.json" Then
Using formstream = Await client.Me.Drive.Items.Item(form.Id).Content.Request.GetAsync
Using reader = New IO.StreamReader(formstream)
forms(form.Name) = reader.ReadToEnd
End Using
End Using
End If
Next
End Sub
End Class
I'm using also this helper class:
Imports System.Net.Http.Headers
Imports Microsoft.Graph
Imports Microsoft.Identity.Client
Public Class AuthenticationHelper
Shared clientId As String = "my-client-id"
Public Shared Scopes As String() = {"User.Read", "Files.ReadWrite.All"}
Public Shared IdentityClientApp As PublicClientApplication = New PublicClientApplication(clientId)
Public Shared TokenForUser As String = Nothing
Public Shared Expiration As DateTimeOffset
Private Shared graphClient As GraphServiceClient = Nothing
Public Shared Function GetAuthenticatedClient() As GraphServiceClient
If graphClient Is Nothing Then
Try
graphClient = New GraphServiceClient(
"https://graph.microsoft.com/v1.0",
New DelegateAuthenticationProvider(
Async Function(requestMessage)
Dim token = Await GetTokenForUserAsync()
requestMessage.Headers.Authorization = New AuthenticationHeaderValue("bearer", token)
requestMessage.Headers.Add("SampleID", "uwp-csharp-apibrowser-sample")
End Function))
Return graphClient
Catch ex As Exception
Debug.WriteLine("Could not create a graph client: " & ex.Message)
End Try
End If
Return graphClient
End Function
Public Shared Async Function GetTokenForUserAsync() As Task(Of String)
Dim authResult As AuthenticationResult
Dim ex As Exception = Nothing
Try
authResult = Await IdentityClientApp.AcquireTokenSilentAsync(Scopes, IdentityClientApp.GetUser("antonio.patricio#agu.gov.br"))
TokenForUser = authResult.AccessToken
Catch ex
If TokenForUser Is Nothing OrElse Expiration <= DateTimeOffset.UtcNow.AddMinutes(5) Then
End If
End Try
If ex IsNot Nothing Then
Try
authResult = Await IdentityClientApp.AcquireTokenAsync(Scopes)
TokenForUser = authResult.AccessToken
Expiration = authResult.ExpiresOn
Catch ex
End Try
End If
Return TokenForUser
End Function
End Class

I checked up samples here and there, and came up with this wrapper class. I think I'll work on it more, but for the moment, it solved the problem mentioned at this post:
Imports System.Net.Http
Imports System.Net.Http.Headers
Imports Microsoft.Graph
Imports Microsoft.Identity.Client
Public Class MsGraph
Private Const baseUrl As String = "https://graph.microsoft.com/v1.0"
Private ReadOnly client_id As String
Private ReadOnly scopes As String()
Private authSuccess As Boolean
Private clientApp As PublicClientApplication
Public Sub New(app_client_id As String, ParamArray app_scopes As String())
client_id = app_client_id
If Not app_scopes.Contains("User.Read", StringComparer.InvariantCultureIgnoreCase) Then
app_scopes = {"User.Read"}.Concat(app_scopes).ToArray
End If
scopes = app_scopes
clientApp = New PublicClientApplication(client_id)
Dim authProvider = New DelegateAuthenticationProvider(AddressOf AuthenticateRequestAsync)
Try
Client = New GraphServiceClient(baseUrl, authProvider)
Catch ex As Exception
Stop
End Try
End Sub
Public ReadOnly Property Client As GraphServiceClient
Public Async Function AuthenticateRequestAsync(request As HttpRequestMessage) As Task
request.Headers.Authorization = New AuthenticationHeaderValue("bearer", Await GetTokenForUserAsync())
End Function
Private Async Function GetTokenForUserAsync() As Task(Of String)
Dim success As Boolean
Dim authResult As AuthenticationResult = Nothing
If clientApp.Users.Any Then
Try
authResult = Await clientApp.AcquireTokenSilentAsync(scopes, clientApp.Users.FirstOrDefault())
success = True
Catch ex As Exception
Stop
End Try
Else
Try
authResult = Await clientApp.AcquireTokenAsync(scopes)
success = True
Catch ex As Exception
Stop
End Try
End If
authSuccess = success
Return authResult?.AccessToken
End Function
End Class

Related

Problem with Crunch occurring after some time FtpWebRequest in vb.net

i have this Code From My Class
The code is working fine, but the memory is increasing, which is causing Crunch to close the program for itself
I tried all the methods and did not find a solution to this problem
Imports System.Net
Imports System.IO
Public Class class1
Private host As String
Private password As String
Private username As String
Dim x As New List(Of String)
Public Sub New(host As String, username As String, password As String)
Me.host = host
Me.username = username
Me.password = password
End Sub
Friend Sub start()
Try
Dim request As FtpWebRequest = WebRequest.Create("ftp://" + Me.host)
request.Timeout = 4000
request.Credentials = New NetworkCredential(Me.username, Me.password)
request.Method = WebRequestMethods.Ftp.ListDirectory
'-----------------------------------
Try
'-----------------------------------
Dim listResponse As FtpWebResponse = request.GetResponse()
' listResponse = request.GetResponse()
Dim reader As StreamReader = New System.IO.StreamReader(listResponse.GetResponseStream())
Dim Filedata As String = reader.ReadToEnd
'-----------------------------------
x.Add(Filedata)
'-----------------------------------
File.WriteAllLines(IO.Directory.GetCurrentDirectory() & "\logs.txt", x)
'-----------------------------------
Threading.Thread.Sleep(1000)
listResponse.Close()
listResponse.Dispose()
request.Abort()
Catch ex As WebException
request.Abort()
Exit Sub
End Try
Catch ex As Exception
Exit Sub
End Try
Exit Sub
End Sub
End Class
i Call this class from the main app
This method call
And use threading
Dim x As New testing(host, username, password)
x.start()
can anyone help me do this i am using Vb.net

Creating a Microsoft Teams Meeting using VB.NET and Microsoft Graph API

I have created a login authentication using the Microsoft Graph API which means after I sign in, as a result, I have the access token of the current user signed-in. My next step is to create a Microsoft Teams Meeting on behalf of the current user signed-in.
I have tried to follow the Microsoft documentation Application POST onlinemeetings, where It shows the required steps to achieve this scenario.
Unfortunately, they didn't provide an example of how it can be achieved in VB.Net (which is not a big deal because I have converted the code to VB).
Currently, I am stuck on sending a POST request to generate this meeting based on the hard coded values when the user clicks on the button.
Please have a look at the code below:
Imports System.IO
Imports System.Net
Imports System.Net.Http
Imports System.Net.Http.Headers
Imports System.Threading.Tasks
Imports Microsoft.Graph
Imports Microsoft.IdentityModel.Clients.ActiveDirectory
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
Public Class _Default
Inherits Page
Private Shared httpClient As HttpClient = New HttpClient()
Private Shared context As AuthenticationContext = Nothing
Private Shared credential As ClientCredential = Nothing
Private Shared graphClient As GraphServiceClient
Private Shared authprovider As IAuthenticationProvider
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
Dim code = HttpContext.Current.Request.QueryString("Code")
If Not Page.IsPostBack Then
If code <> "" Then
Dim url = "https://login.microsoftonline.com/common/oauth2/v2.0/token"
Dim myParameters = "grant_type=authorization_code&code=" & code & "&redirect_uri=https://localhost:4312/&client_id=CLIENTID&client_secret=CLIENTSECRET"
Dim wb = New WebClient
wb.Headers(HttpRequestHeader.ContentType) = "application/x-www-form-urlencoded"
Dim response = wb.UploadString(url, "POST", myParameters)
responseToken.Text = response
Success.Text = "O365 login successful. Below is the response token"
Dim SurroundingClass = JsonConvert.DeserializeObject(Of SurroundingClass)(response)
Dim rss As JObject = JObject.Parse(response)
Dim token = rss.SelectToken("access_token")
Dim res = GetUsers(token)
End If
End If
End Sub
Private Shared Async Function GetUsers(ByVal result As String) As Task(Of String)
Try
Dim users As String = Nothing
Dim querystring As String = "api-version=1.6"
Dim uri = "https://graph.microsoft.com/v1.0/me"
httpClient.DefaultRequestHeaders.Authorization = New AuthenticationHeaderValue("Bearer", result)
httpClient.DefaultRequestHeaders.Accept.Add(New MediaTypeWithQualityHeaderValue("application/json"))
Dim User = GetMeAsync().Result
Console.WriteLine($"Welcome {User.DisplayName}!\n")
Dim getResult = Await httpClient.GetAsync(uri)
If getResult.Content IsNot Nothing Then
users = Await getResult.Content.ReadAsStringAsync()
End If
Return users
Catch ex As Exception
Throw ex
End Try
End Function
Protected Sub tes_Click(sender As Object, e As EventArgs)
Try
'Dim fr As System.Net.HttpWebRequest
Dim client_id = ""// App Client ID'
Dim uri = "https://localhost:4312/"
Dim targetURI As String = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=" & client_id &
"&redirect_uri=" & uri & "&response_type=code&scope=openid+Mail.Read"
Response.Redirect(targetURI)
Catch ex As System.Net.WebException
'Error in accessing the resource, handle it
End Try
End Sub
Public Shared Async Function CreateMeeting() As Task(Of OnlineMeeting)
Try
graphClient = New GraphServiceClient(authprovider)
Dim onlineMeeting = New OnlineMeeting With {
.StartDateTime = DateTimeOffset.Parse("2020-04-23T21:33:30.8546353+00:00"),
.EndDateTime = DateTimeOffset.Parse("2020-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 = "MYID"
}
}
}
}
}
Dim encodings As New UTF8Encoding
Dim serializer As New JavaScriptSerializer()
Dim arrayJson As String = serializer.Serialize(onlineMeeting)
Dim result As String = Nothing
Dim postRequest As HttpWebRequest = DirectCast(WebRequest.Create("https://graph.microsoft.com/v1.0/me/onlineMeetings"), HttpWebRequest)
postRequest.Method = "POST"
postRequest.ContentType = "application/json"
httpClient.DefaultRequestHeaders.Authorization = New AuthenticationHeaderValue("Bearer", Token)
If postRequest.Method = "POST" Then
Dim parsedContent As String = JsonConvert.SerializeObject(onlineMeeting)
Dim encoding As ASCIIEncoding = New ASCIIEncoding()
Dim bytes As Byte() = encoding.GetBytes(parsedContent)
Dim newStream As Stream = postRequest.GetRequestStream()
newStream.Write(bytes, 0, bytes.Length)
newStream.Close()
End If
Dim createMeetings = Await graphClient.Communications.OnlineMeetings.Request().AddAsync(onlineMeeting)
Console.WriteLine("The meeting has been created")
Return createMeetings
Catch ex As ServiceException
Console.WriteLine($"Error while creating the meeting: {ex.Message}")
Return Nothing
End Try
End Function
Public Sub InvokeMeeting(sender As Object, e As EventArgs)
Try
Dim testing = CreateMeeting()
Catch ex As Exception
End Try
End Sub
PS: I have added the permission required to call this API to be able to create a Teams meeting.
Any suggestions on how to achieve the following scenario?
Any help will be greatly appreciated.
Thank you!

vb.net I got (Unable to read data from the transport connection) error message from socket

I'm trying to use a non-synchronous connection server on the client, but occasionally I get an error message on the client. How can I solve the problem?
How to change to callback?
Error code :
unable to read data from the transport connection the connection was
closed
at System.Net.Sockets.NetworkStream.EndRead(IAsyncResult asyncResult)
at TFD_SoftPhone.MD_Function.ConnectionInfo.DoReadData(IAsyncResult
result)
Public Class ConnectionInfo
Private _AppendMethod As Action(Of String)
Private _Client As TcpClient
Private _Stream As NetworkStream
Private _LastReadLength As Integer
Private _Buffer(5120) As Byte
Public ReadOnly Property AppendMethod As Action(Of String)
Get
Return _AppendMethod
End Get
End Property
Public ReadOnly Property Client As TcpClient
Get
Return _Client
End Get
End Property
Public ReadOnly Property Stream As NetworkStream
Get
Return _Stream
End Get
End Property
Public ReadOnly Property LastReadLength As Integer
Get
Return _LastReadLength
End Get
End Property
Public Sub New(address As IPAddress, port As Integer, append As Action(Of String))
_AppendMethod = append
_Client = New TcpClient
Try
_Client.SendTimeout = 45000
_Client.ReceiveTimeout = 45000
_Client.Connect(address, port)
_Stream = _Client.GetStream
Catch ex As Exception
If _Client.Connected Then _Client.Close()
If Not IsNothing(_Client) Then _Client = Nothing
If Not IsNothing(_Stream) Then _Stream = Nothing
End Try
End Sub
Public Sub AwaitData()
Try
If Not IsNothing(_Stream) Then
_Stream.ReadTimeout = 45000
_Stream.WriteTimeout = 45000
_Stream.BeginRead(_Buffer, 0, _Buffer.Length, AddressOf DoReadData, Me)
End If
Catch ex As Exception
End Try
End Sub
Public Sub Close()
If _Client IsNot Nothing Then _Client.Close()
_Client = Nothing
_Stream = Nothing
End Sub
Private Sub DoReadData(result As IAsyncResult)
Dim info As ConnectionInfo = CType(result.AsyncState, ConnectionInfo)
Try
If info._Stream IsNot Nothing AndAlso info._Stream.CanRead Then
info._LastReadLength = info._Stream.EndRead(result)
ReDim Preserve _Buffer(info._LastReadLength - 1)
If info._LastReadLength > 0 Then
Dim message As String = Encoding.GetEncoding("Big5").GetString(info._Buffer)
info._AppendMethod(message)
ReDim Preserve _Buffer(5120)
Else
End If
info.AwaitData()
Else
End If
Catch ex As Exception
info._LastReadLength = -1
info._AppendMethod(ex.Message)
End Try
End Sub
End Class

Simple VB.Net text base communication server

I try for one week to provide a PHP application (client) and a VB.Net application (server) via text messages (JSON).
I must therefore open a socket server in VB.Net, read the client message and close the connection. Of course by managing connections from clients in separate threads since PHP may well send multiple queries simultaneously.
This is a trivial task in Java, as I usually do, but and a VB.Net I tried many solutions found on StackOverflow and CodeProject, but none is exactly what I want to achieve .
Finally I think I found something interesting !
Based on the post Writing a Simple HTTP Server in VB.Net from Patrick Santry, I have a functional class :
Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports System.Threading
Public Class Server
#Region "Declarations"
Private Shared singleServer As Server
Private Shared blnFlag As Boolean
Private LocalTCPListener As TcpListener
Private LocalPort As Integer
Private LocalAddress As IPAddress = GetIPAddress()
Private ServerThread As Thread
#End Region
#Region "Properties"
Public Property ListenPort() As Integer
Get
Return LocalPort
End Get
Set(ByVal Value As Integer)
LocalPort = Value
End Set
End Property
Public ReadOnly Property ListenIPAddress() As IPAddress
Get
Return LocalAddress
End Get
End Property
#End Region
#Region "Methods"
Private Function GetIPAddress() As IPAddress
With System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName())
If .AddressList.Length > 0 Then
Return New IPAddress(.AddressList.GetLowerBound(0))
End If
End With
Return Nothing
End Function
Friend Shared Function getServer(ByVal LocalPort As Integer, ByVal Optional LocalAddress As String = Nothing) As Server
If Not blnFlag Then
singleServer = New Server
If Not LocalAddress Is Nothing Then
Server.singleServer.LocalAddress = IPAddress.Parse(LocalAddress)
End If
If Not LocalPort = 0 Then
Server.singleServer.LocalPort = LocalPort
End If
blnFlag = True
Return Server.singleServer
Else
Return Server.singleServer
End If
End Function
Public Sub StartServer()
Try
LocalTCPListener = New TcpListener(LocalAddress, LocalPort)
LocalTCPListener.Start()
ServerThread = New Thread(AddressOf StartListen)
serverThread.Start()
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
Public Overloads Sub SendResponse(ByVal sData As String, ByRef thisSocket As Socket)
SendResponse(Encoding.UTF8.GetBytes(sData), thisSocket)
End Sub
Public Overloads Sub SendResponse(ByVal bSendData As [Byte](), ByRef thisSocket As Socket)
Dim iNumBytes As Integer = 0
If thisSocket.Connected Then
If (iNumBytes = thisSocket.Send(bSendData, bSendData.Length, 0)) = -1 Then
' socket error can't send packet
Else
' number of bytes sent.
End If
Else
' connection dropped.
End If
End Sub
Private Sub New()
' create a singleton
End Sub
Private Sub StartListen()
Do While True
' accept new socket connection
Dim mySocket As Socket = LocalTCPListener.AcceptSocket
If mySocket.Connected Then
Dim ClientThread As Thread = New Thread(Sub() Me.ProcessRequest(mySocket))
ClientThread.Start()
End If
Loop
End Sub
Private Sub ProcessRequest(ByRef mySocket As Socket)
Dim bReceive() As Byte = New [Byte](1024) {}
Dim i As Integer = mySocket.Receive(bReceive, bReceive.Length, 0)
Dim sRequest = Encoding.UTF8.GetString(bReceive)
Dim sResponse As String
sResponse = "Your message was : " & sRequest
SendResponse(sResponse, mySocket)
mySocket.Close()
End Sub
Public Sub StopServer()
Try
LocalTCPListener.Stop()
ServerThread.Abort()
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
#End Region
End Class
It remains for me to process the request and generate the response in the processRequest method.

Problems with threading and WebRequests

I'm sure there's some other threads about this, but i think i need a threading for dummies or something.
My problem: I want to fetch a value by a WebRequest and display it. My code looks something like this:
Foo = New Fetcher()
AddHandler Foo.HasResult, AddressOf Me.FetchValue
Private Sub FetchValue()
If Foo.HasErrors Then
MyTextBlock.Text = "ERROR"
Exit Sub
End IF
MyTextBlock.Text = Foo.Value 'Here it crashes.....
End sub
Public Class Fetcher
Public Event HasResult(ByVal F As Fetcher)
Public WasError As Boolean = True
Public Value As String = ""
Public Sub New()
Dim request As WebRequest = WebRequest.Create("theurl")
request.BeginGetResponse(New AsyncCallback(AddressOf Me.GetValueAnswer), request)
End Sub
Private Sub GetValueAnswer(asynchronousResult As IAsyncResult)
Dim request As HttpWebRequest = asynchronousResult.AsyncState
If Not request Is Nothing Then
Try
Dim response As WebResponse = request.EndGetResponse(asynchronousResult)
Using stream As Stream = response.GetResponseStream()
Using reader As New StreamReader(stream, System.Text.Encoding.UTF8)
Dim responseString = reader.ReadToEnd()
Me.Value = ResponseString
Me.WasError = False
End Using
End Using
Catch(Exception Ex)
Me.WasError = True 'Not needed in this example, i know...
End Try
End If
RaiseEvent HasResult(Me)
End Sub
End Class
This is a little bit simplified, but it's the same error as well.
At the line with the comment "Here it crashes....." i get an exception with "The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))"
I can how ever see that my result is fetched when i explore the Foo.
So, how is the right way to do this?
(And yes; if I enter a bad URL or something so that "WasError" is true, of course i get the same exception when i try to set my textblock to "ERROR")
EDIT: After some really strong words, blood sweat and tears, i came up with this change to the FetchValue(), and now it finally works....
If Me.MyTextBlock.Dispatcher.HasThreadAccess Then
If Foo.HasErrors Then
MyTextBlock.Text = "ERROR"
Exit Sub
End IF
MyTextBlock.Text = Foo.Value
Else
Me.MyTestBlock.Dispatcher.RunAsync(Core.CoreDispatcherPriority.Normal, _
AddressOf Me.FetchValue)
End If
I do how ever get a warning on the row in else that says "Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the Await operator to the result of the call."
Any ideas for how to make this warning go away?
It's far, far easier to do this with HttpClient and Async/Await.
My VB is rusty, but here goes:
Public Class Fetcher
Public Result As Task(of String)
Public Sub New()
Dim client As HttpClient = New HttpClient()
Result = client.GetStringAsync("theurl")
End Sub
End Class
Usage:
Foo = New Fetcher()
Try
Dim data As String = Await Foo.Result
MyTextBlock.Text = data
Catch(Exception Ex)
MyTextBlock.Text = "ERROR"
End Try
Note that the Task<T> type handles return values, error conditions, and completion notification. So all the posted code in your Fetcher class is pretty much unnecessary if you use HttpClient with Task<T>. If your Fetcher class doesn't do anything else, then you should probably just remove it entirely:
Dim client As HttpClient = New HttpClient()
Try
MyTextBlock.Text = Await client.GetStringAsync("theurl")
Catch(Exception Ex)
MyTextBlock.Text = "ERROR"
End Try