Open Closed

Accessing API from a VB.NET Client app, guide on how to authenticate, GET / POST data etc #8573


User avatar
0
wingers created

Hi

I need to be able to access the API's of my ABP website from a VB.NET WinForms application, and I want to do it correctly and securely.

So hoping for some guidance as can't find any tutorials or sample apps to help me.

I have created a sample solution with the sample CRUD pages (book store) to use to learn more

  1. Do i need to include OpenIddict in my project or not?

  2. How do I securely connect to the API from my app, what config do I need to do in the app (if any) first and how do I then access for example a list of books and add a new book?

Anything else you need me to answer to allow a complete response please let me know as I am running out of questions already and only been using it a short while!

Thank you (and happy new year!)

  • Template: app-nolayers
  • Created ABP Studio Version: 0.9.18
  • Current ABP Studio Version: 0.9.18
  • Multi-Tenancy: No
  • UI Framework: mvc
  • Theme: leptonx
  • Theme Style: system
  • Database Provider: ef
  • Database Management System: sqlserver
  • Optional Modules:
    • GDPR
    • LanguageManagement
    • AuditLogging

22 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    You need to add openiddict to your project. It will used for issuing and validate the token.

    1. Add openiddict to your project.
    2. Create a client and use it to get a token from your website.
    3. Get a token.
    4. Use the token to call the API(bearer token).

  • User Avatar
    0
    wingers created

    Thank you very much, that is most helpful.

    Could you possibly expand further as to how from a vb.net or even c# winforms app how I then firstly authenticate and then GET information or POST new information - using your sample CRUD books as an example

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    I'm not familiar with VB and Winforms.

    You can use https://github.com/IdentityModel/IdentityModel to geta token in your c# project.

    see https://github.com/abpframework/abp/blob/dev/modules/openiddict/app/OpenIddict.Demo.Client.Console/Program.cs

  • User Avatar
    0
    wingers created

    Your screenshots above look different than I am seeing on latest ABP as I also have a dropdown box titled ApplicatonType which can be set to Web or Native - so can you update your response to suit latest released version of ABP as per information I provided in initial post - thanks

  • User Avatar
    0
    wingers created

    Can't get it working, tried choosing native for the step your screen shot doesn't show, and replicated all other settings, but testing from postman fails.

    Also you have MyProjectName appearing under scopes, I don't have name of my project here, so how do I add it, what are the settings needed to do so?

  • User Avatar
    0
    wingers created

    Using code from sample console app you provided I get Unauthorized exception

  • User Avatar
    0
    wingers created

    I have been trying to resolve this all day so below is summary of what has been done.

    1 ) For testing this I am working with an abp solution created from scratch with abp studio configured as below:

    • Application (single layer) app-nolayers
    • MVC / Razor Pages
    • Entity > SQL
    • Enable Multi-tenancy - NOT CHECKED
    • LeptonX
    • optional modules = audit logging, GDPR, language management and OpenIddict UI
    • social logins - NOT CHECKED
    • sample crud pages - CHECKED - so I can use book store as my test api

    2 ) This has been uploaded to my hosting and is working fine, via website directly and via swagger I can view and add books to the book store

    3 ) Using below method in VB I can connect to the API and retrieve book information - this is using cookie and anti-forgery e.g. method primarily for browser scenarios so not a solution I want to use for my VB.NET winforms app but does at least show I can connect okay and API is working

     ' working method but this uses the Cookie + Anti-Forgery which is Primarily for Browser Scenarios
     Private Async Sub btnLoginWeb_Click(sender As Object, e As EventArgs) Handles btnLoginWeb.Click
         ' 1) Set up a handler with CookieContainer to store auth cookies
         Dim handler As New HttpClientHandler() With {
             .UseCookies = True,
             .CookieContainer = New CookieContainer()
         }
    
         Using client As New HttpClient(handler)
             ' The base URL of your application
             client.BaseAddress = New Uri("https://pcassist-testing.premiumasp.net/")
    
             ' 2) GET the login page to retrieve the anti-forgery token
             Dim loginPageResponse As HttpResponseMessage = Await client.GetAsync("Account/Login")
             If Not loginPageResponse.IsSuccessStatusCode Then
                 MessageBox.Show($"Failed to load login page. Status: {loginPageResponse.StatusCode}")
                 Return
             End If
    
             Dim loginPageHtml As String = Await loginPageResponse.Content.ReadAsStringAsync()
    
             ' 3) Parse out __RequestVerificationToken from the HTML
             Dim tokenValue As String = ExtractAntiForgeryToken(loginPageHtml)
             If String.IsNullOrEmpty(tokenValue) Then
                 MessageBox.Show("Could not find __RequestVerificationToken in login page HTML.")
                 Return
             End If
    
             ' 4) Build the form data that your site expects for login
             '    Adjust the field names (e.g., "UsernameOrEmailAddress", "Password") 
             '    to exactly match what the login form uses.
             Dim loginForm As New Dictionary(Of String, String) From {
                 {"__RequestVerificationToken", tokenValue},
                 {"UsernameOrEmailAddress", "admin"},    ' or your real username
                 {"Password", "REMOVED"},                 ' or your real password
                 {"RememberMe", "false"}
             }
    
             ' 5) POST the login form
             Dim loginContent As New FormUrlEncodedContent(loginForm)
             Dim loginResponse As HttpResponseMessage = Await client.PostAsync("Account/Login", loginContent)
             Dim loginResponseBody As String = Await loginResponse.Content.ReadAsStringAsync()
    
             If loginResponse.IsSuccessStatusCode Then
                 ' 6) Now the CookieContainer has the session/auth cookie
                 '    Let's call the protected endpoint using the same client
                 Dim dataResponse As HttpResponseMessage = Await client.GetAsync("api/app/book")
                 Dim dataResponseBody As String = Await dataResponse.Content.ReadAsStringAsync()
    
                 If dataResponse.IsSuccessStatusCode Then
                     MessageBox.Show("Protected data: " & dataResponseBody)
                 Else
                     MessageBox.Show($"API call failed. Status: {dataResponse.StatusCode}, Body: {dataResponseBody}")
                 End If
             Else
                 MessageBox.Show($"Login failed. Status: {loginResponse.StatusCode}, Body: {loginResponseBody}")
             End If
         End Using
     End Sub`
    

    4 ) I then tried using below method to retrieve the token - not sure if this is correct as perhaps uses identityserver rather than openiddict but with code below and within postman I can retrieve a token okay - so assume this means I have something setup correctly!???.....

    Private Async Sub btnGetToken_Click(sender As Object, e As EventArgs) Handles btnGetToken.Click
    
        ' 1) Define your token endpoint (commonly /connect/token with ABP + IdentityServer)
        Dim tokenUrl As String = "https://pcassist-testing.premiumasp.net/connect/token"
    
        ' 2) Build up the form fields as per Resource Owner Password Flow
        ' Adjust the client_id, client_secret, scope, and credentials to your ABP config
        Dim formData As New Dictionary(Of String, String) From {
            {"grant_type", "password"},
            {"client_id", "MyDesktopApp"},    ' or your actual client_id
            {"client_secret", "removed*"},        ' or your actual client_secret
            {"scope", "offline_access clienttest2"},             ' or "offline_access MyProject" if you also want a refresh token
            {"username", "admin"},              ' ABP username
            {"password", "removed*"}             ' ABP password
        }
    
        ' 3) Use FormUrlEncodedContent for x-www-form-urlencoded
        Using content As New FormUrlEncodedContent(formData)
            Dim response = Await client.PostAsync(tokenUrl, content)
            Dim responseString = Await response.Content.ReadAsStringAsync()
    
            If response.IsSuccessStatusCode Then
                ' 4) Deserialize the JSON to get the access token
                Dim tokenObj = JsonConvert.DeserializeObject(Of TokenResponse)(responseString)
    
                If tokenObj IsNot Nothing AndAlso Not String.IsNullOrEmpty(tokenObj.access_token) Then
                    bearerToken = tokenObj.access_token
    
                    ' 5) Set the Authorization header for future requests
                    client.DefaultRequestHeaders.Authorization =
                        New AuthenticationHeaderValue("Bearer", bearerToken)
    
                    MessageBox.Show("Token retrieved & set in HttpClient Authorization header!")
                Else
                    MessageBox.Show("Token response did not contain an access_token.")
                End If
            Else
                MessageBox.Show($"Error: {response.StatusCode}{vbCrLf}{responseString}")
            End If
        End Using
    End Sub
    
    

    5 ) BUT if I then try to create a new book using this token either via my vb.net code (below) or via postman i get an error

    The result from code below seems to be returning the HTML of the login page so it is like it is ignoring the token?

    In postman it gives me a 400 bad request error

    Private Async Sub btnSendData_Click(sender As Object, e As EventArgs) Handles btnSendData.Click
        If String.IsNullOrEmpty(bearerToken) Then
            MessageBox.Show("No token set! Click 'Get Token' first.")
            Return
        End If
    
        ' testing only
        'For Each header In client.DefaultRequestHeaders
        '    Debug.WriteLine($"Header: {header.Key} = {String.Join(", ", header.Value)}")
        'Next
    
        Dim apiUrl As String = "https://pcassist-testing.premiumasp.net/api/app/book"
    
        Dim bookData = New With {
            .name = "my test book 6",
            .publishDate = "2025-01-03"
        }
        Dim jsonBody As String = JsonConvert.SerializeObject(bookData)
    
        Using content As New StringContent(jsonBody, Encoding.UTF8, "application/json")
            Dim postResponse = Await client.PostAsync(apiUrl, content)
            Dim resultString = Await postResponse.Content.ReadAsStringAsync()
            MessageBox.Show($"Status: {postResponse.StatusCode}{vbCrLf}{resultString}")
        End Using
    
    End Sub
    

    So what am I missing here?

  • User Avatar
    0
    wingers created

    UPDATE: after checking server logs I found the below:

    2025-01-03 18:48:16.938 +01:00 [WRN] The required antiforgery header value "RequestVerificationToken" is not present. 2025-01-03 18:48:16.938 +01:00 [INF] Authorization failed for the request at filter 'Volo.Abp.AspNetCore.Mvc.AntiForgery.AbpAutoValidateAntiforgeryTokenAuthorizationFilter'.

    So indicates antiforgery is causing it to fail - so why? and what do I need to change to get it working?

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    after checking server logs I found the below:

    Please remove all Cookies from your HTTP request.

    The anti-forgery will check if the current request contains the XSRF-TOKEN cookie.

    https://abp.io/docs/latest/framework/infrastructure/csrf-anti-forgery

  • User Avatar
    0
    wingers created

    I am not sending cookies in my HTTP request, even if turned off for httpclient still doesn't work. And when testing with postman can't see any way to turn them off, I can clear them, but this still doesn't resolve problem....

  • User Avatar
    0
    wingers created

    I can provide source code or access to where it is hosted if that means it would allow you to help me more

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Please share full debug logs of this request(made by postman).

    https://pcassist-testing.premiumasp.net/api/app/book

    liming.ma@volosoft.com

    Thanks.

  • User Avatar
    0
    wingers created

    Please tell me how to get the information you require, do you just want me to run a GET against that url? if so then that is returning 200 OK but is returning HTML of login page rather than the API results

    You can run it yourself? site still using default ABP credentials for now

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    1. Enable debug log level
    2. Make the api/app/book in your Postman with your access token.
    3. Share the logs.txt file with liming.ma@volosoft.com

    .MinimumLevel.Debug()

    loggerConfiguration
        .MinimumLevel.Debug()
        .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
        .Enrich.FromLogContext()
        .WriteTo.Async(c => c.File("Logs/logs.txt"))
        .WriteTo.Async(c => c.Console())
        .WriteTo.Async(c => c.AbpStudio(services));
    
  • User Avatar
    0
    wingers created

    i'm using https://web.postman.co/workspace and can't see where you set the log level, are you using the website or the app? so I can do as requested please?

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Set log level in your c# application.

    Program.cs file.

  • User Avatar
    0
    wingers created

    You asked me for logs from running the request in postman? postman is accessed via website or app. NOT run in my c# application. So please clarify what you want me to provide. I have now downloaded postman app rather than website so can run any tests in that. OR are you asking me to make changes in my client app I am trying to get working (which is vb not c#)

  • User Avatar
    0
    wingers created

    p.s. 1:30am in morning here now, so if you provide instructions either here or by email - darren.rose@pcassistonline.co.uk I will follow them tomorrow and respond to you with all you need

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    You need to change the log level of your website(https://pcassist-testing.premiumasp.net/).

    Postman is just used to send HTTP requests (API/app/book).

    We need the logs.txt of your website.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

  • User Avatar
    0
    wingers created

    I understand now, will do so in the morning and email them to you. thank you

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    ok, Please share full Logs.txt file.

Made with ❤️ on ABP v9.1.0-preview. Updated on January 02, 2025, 07:06