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
Do i need to include OpenIddict in my project or not?
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)
-
0
-
0
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
-
0
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
-
0
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
-
0
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?
-
0
-
0
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?
-
0
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?
-
0
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
-
0
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....
-
0
I can provide source code or access to where it is hosted if that means it would allow you to help me more
-
0
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.
-
0
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
-
0
hi
- Enable debug log level
- Make the
api/app/book
in your Postman with your access token. - 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));
-
0
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?
-
0
-
0
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#)
-
0
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
-
0
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. -
0
-
0
I understand now, will do so in the morning and email them to you. thank you
-
0
ok, Please share full Logs.txt file.