VB. Net oauth2 / auth-flow winForms - PullRequest
0 голосов
/ 10 июля 2020

Я потратил семь дней, пытаясь подключиться к Xero с помощью VB. Net oauth2 / auth-flow WinForms с минимальным количеством ссылок. Для других, у которых возникают проблемы с попыткой заставить это работать, вот мой код.

Получение AccessToken и RefreshToken:

Private Async Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        'OAuth2.0
        Dim XeroConnect As Boolean
        xeroOAuth2 = New clsXeroAuth2
        'make first connection and save refresh token for subsequent offline access codes
        XeroConnect = Await xeroOAuth2.oauth2()
        'get new access token using existing refresh token
        XeroConnect = xeroOAuth2.getOfflineAccessCodeandRefreshToken()
    End Sub

clsXeroAuth2 Class Code

Imports System.IO
Imports System.Net
Imports System.Text
Imports Newtonsoft.Json.Linq
Imports RestSharp

Public Class clsXeroAuth2 : Implements IDisposable
    Private xeroCode As String
    Public accessToken As String
    Public accessTokenExpires As DateTime
    Private refreshToken As String
    Public tenantID As String
    Private stopListening As Boolean = True
    Private callbackurl As String = "http://localhost:5000/signin/"
    Private xeroClientId As String = "???"
    Private xeroClientSecret As String = "???"

    Public Async Function oauth2() As Task(Of Boolean)
        Dim redirectUri As String = String.Format(callbackurl)
        Dim listener = New HttpListener()
        listener.Prefixes.Add(redirectUri)

        Try
            listener.Start()
            stopListening = False
        Catch hlex As HttpListenerException
            Return False
        End Try

        Dim urlCallBackFormat As String = WebUtility.UrlEncode(String.Format(callbackurl))
        Dim url As String = "https://login.xero.com/identity/connect/authorize?response_type=code&state=OzEPHd1GSvkwx0fnJ8023A&client_id=" & xeroClientId & "&scope=offline_access%20openid%20profile%20email%20accounting.settings%20accounting.transactions&redirect_uri=" & urlCallBackFormat
        Process.Start(url)

        While listener.IsListening
            Dim context As Object = Await listener.GetContextAsync()
            Try
                Await ProcessRequestAsync(context)
                stopListening = True
            Catch ex As Exception
                Console.WriteLine("# EXCEPTION #   " + ex.StackTrace)
            End Try
            If stopListening = True Then listener.Stop()
        End While

        listener.Stop()
        listener.Close()

        'now get access token using code
        Dim Basic As String = "Basic " & getBase64Encoded()

        Dim getTenant As RestClient = New RestClient("https://identity.xero.com/connect/token")
        getTenant.Timeout = -1
        Dim requestTenant = New RestRequest(Method.POST)
        requestTenant.AddHeader("authorization", Basic)
        requestTenant.AddHeader("Content-Type", "application/x-www-form-urlencoded")
        requestTenant.AddParameter("grant_type", "authorization_code")
        requestTenant.AddParameter("code", xeroCode)
        requestTenant.AddParameter("redirect_uri", String.Format(callbackurl))

        Dim responseTenant As IRestResponse = getTenant.Execute(requestTenant)
        Dim myJObject = JObject.Parse("{'results':" & responseTenant.Content & "}")
        Dim tokenError As String
        For Each Row In myJObject("results").ToList()
            Select Case DirectCast(Row, JProperty).Name
                Case "access_token"
                    accessToken = DirectCast(Row, JProperty).Value
                    Debug.Print(accessToken)
                Case "expires_in"
                    accessTokenExpires = DateAdd(DateInterval.Second, CDbl(DirectCast(Row, JProperty).Value), Now())
                Case "refresh_token"
                    refreshToken = DirectCast(Row, JProperty).Value
                Case "error"
                    tokenError = DirectCast(Row, JProperty).Value
            End Select
        Next

        Try
            getTenant = New RestClient("https://api.xero.com/connections")
            getTenant.Timeout = -1
            requestTenant = New RestRequest(Method.GET)
            requestTenant.AddHeader("Authorization", "Bearer " & accessToken)
            requestTenant.AddHeader("Content-Type", "application/json")
            responseTenant = getTenant.Execute(requestTenant)
            myJObject = JObject.Parse("{'results':" & responseTenant.Content & "}")

            Dim commToken As JToken
            Dim commValue As String
            Dim dt As DataTable = New DataTable("tenants")
            Dim c As DataColumn
            c = New DataColumn("tenantID", System.Type.GetType("System.String"))
            dt.Columns.Add(c)
            c = New DataColumn("tenantName", System.Type.GetType("System.String"))
            dt.Columns.Add(c)

            Dim r As DataRow
            For Each Row In myJObject("results").ToList()
                r = dt.NewRow
                commToken = Row("tenantId")
                commValue = DirectCast(commToken, JValue).Value
                r("tenantID") = commValue
                commToken = Row("tenantName")
                commValue = DirectCast(commToken, JValue).Value
                r("tenantName") = commValue
                dt.Rows.Add(r)
            Next

            Dim selectedTenant As String = ""

            If dt.Rows.Count > 1 Then
                'display the connections and ask user which they wish to use today
                Using frmTenants As New frmXeroSelectTenant(dt)
                    frmTenants.ShowDialog()
                    selectedTenant = frmTenants.selectedTenant
                End Using
            ElseIf dt.Rows.Count = 1 Then
                selectedTenant = dt(0)(0)
            Else
                MsgBox("Xero Intergration requires a Xero Account to be authorised to connect to!", MsgBoxStyle.Exclamation, "Authorised Xero Account Required...")
                Return False
            End If

            tenantID = selectedTenant

        Catch ex As Exception

            Debug.Print(ex.Message & " " & ex.StackTrace)
            Return False

        End Try

        Return True
    End Function
    
    Private Async Function ProcessRequestAsync(ByVal context As HttpListenerContext) As Task(Of Boolean)
        Dim body = Await New StreamReader(context.Request.InputStream).ReadToEndAsync()
        Dim request As HttpListenerRequest = context.Request

        If request.RawUrl.StartsWith("/signin") Then
            Dim options = context.Request.QueryString
            xeroCode = context.Request.QueryString("code")
            Dim state = context.Request.QueryString("state")
            Dim b As Byte() = Encoding.UTF8.GetBytes("You may close this web page now")
            context.Response.StatusCode = 200
            context.Response.KeepAlive = False
            context.Response.ContentLength64 = b.Length
            Dim output = context.Response.OutputStream
            Await output.WriteAsync(b, 0, b.Length)
            context.Response.Close()
        End If
    End Function
    
    Private Function getBase64Encoded() As String
        Dim base64Decoded As String = xeroClientId & ":" & xeroClientSecret
        Dim base64Encoded As String
        Dim data As Byte()
        data = System.Text.ASCIIEncoding.ASCII.GetBytes(base64Decoded)
        base64Encoded = System.Convert.ToBase64String(data)
        Return base64Encoded
    End Function
    
    Public Function getOfflineAccessCodeandRefreshToken() As Boolean
        Dim Basic As String = "Basic " & getBase64Encoded()
        Dim getTenant As RestClient = New RestClient("https://identity.xero.com/connect/token")
        getTenant.Timeout = -1
        Dim requestTenant = New RestRequest(Method.POST)
        requestTenant.AddHeader("authorization", Basic)
        requestTenant.AddHeader("Content-Type", "application/x-www-form-urlencoded")
        requestTenant.AddParameter("grant_type", "refresh_token")
        requestTenant.AddParameter("refresh_token", refreshToken)

        Dim responseTenant As IRestResponse = getTenant.Execute(requestTenant)
        Dim myJObject = JObject.Parse("{'results':" & responseTenant.Content & "}")
        Dim tokenRrror As String

        For Each Row In myJObject("results").ToList()
            Select Case DirectCast(Row, JProperty).Name
                Case "access_token"
                    accessToken = DirectCast(Row, JProperty).Value
                Case "expires_in"
                    accessTokenExpires = DateAdd(DateInterval.Second, CDbl(DirectCast(Row, JProperty).Value), Now())
                Case "refresh_token"
                    refreshToken = DirectCast(Row, JProperty).Value
                Case "error"
                    tokenRrror = DirectCast(Row, JProperty).Value
                    Return False
            End Select
        Next
        Return True
    End Function

End Class

Create форма под названием [b] frmXeroSelectTenant [/ b] со списком под названием [b] listTenants [/ b] для перечисления, если пользователь Xero назначил более одного приложения [b] Tenant [/ b].

Public Class frmXeroSelectTenant
    Public selectedTenant As String
    Public Sub New(dt As DataTable)
        InitializeComponent()
        Me.listTenants.DisplayMember = "tenantName"
        Me.listTenants.ValueMember = "tenantID"
        Me.listTenants.DataSource = dt
    End Sub

    Private Sub listTenants_DoubleClick(sender As Object, e As EventArgs) Handles listTenants.DoubleClick
        selectedTenant = listTenants.SelectedValue
        Me.Close()
    End Sub
End Class

Надеюсь, это кому-то поможет. Сообщите мне об ошибках.

...