I have my OpenID provider running. I have also my dockerized dataverse running. I set the OpenID jvm options and see the OpenID button in the dataverse login view. My authorize endpoint is called properly but my token endpoint is not called at all.
The following is the output of the .well-know/openid-configuration endpoint:
{
  "issuer": "http://host.docker.internal:3000/api/auth",
  "authorization_endpoint": "http://host.docker.internal:3000/api/auth/authorize",
  "token_endpoint": "http://host.docker.internal:3000/api/auth/oauth-access-token",
  "userinfo_endpoint": "http://host.docker.internal:3000/api/auth/oauth-user-profile",
  "jwks_uri": "http://host.docker.internal:3000/api/auth/.well-known/jwks.json",
  "response_types_supported": ["code", "token", "id_token", "code id_token"],
  "subject_types_supported": ["public", "pairwise"],
  "id_token_signing_alg_values_supported": ["RS256", "ES256", "HS256"]
}
And this is the log I see in the dataverse container logs:
[#|2024-03-11T08:35:03.773+0000|WARNING|Payara 6.2023.8|jakarta.enterprise.resource.webcontainer.faces.lifecycle|_ThreadID=76;_ThreadName=http-thread-pool::http-listener-1(1);_TimeMillis=1710146103773;_LevelValue=900;|
dataverse         |   #{OAuth2Page.exchangeCodeForToken()}: /oauth2/callback.xhtml @14,74 action="#{OAuth2Page.exchangeCodeForToken()}": java.net.ConnectException: Connection refused
dataverse         | jakarta.faces.FacesException: #{OAuth2Page.exchangeCodeForToken()}: /oauth2/callback.xhtml @14,74 action="#{OAuth2Page.exchangeCodeForToken()}": java.net.ConnectException: Connection refused
dataverse         |     at com.sun.faces.application.ActionListenerImpl.getNavigationOutcome(ActionListenerImpl.java:83)
dataverse         |     at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:62)
dataverse         |     at jakarta.faces.component.UIViewAction.broadcast(UIViewAction.java:506)
dataverse         |     at jakarta.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:858)
dataverse         |     at jakarta.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1332)
dataverse         |     at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:56)
dataverse         |     at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:72)
dataverse         |     at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:159)
dataverse         |     at jakarta.faces.webapp.FacesServlet.executeLifecyle(FacesServlet.java:691)
dataverse         |     at jakarta.faces.webapp.FacesServlet.service(FacesServlet.java:449)
dataverse         |     at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1554)
dataverse         |     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:331)
dataverse         |     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:211)
dataverse         |     at org.glassfish.tyrus.servlet.TyrusServletFilter.doFilter(TyrusServletFilter.java:83)
dataverse         |     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:253)
dataverse         |     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:211)
dataverse         |     at org.ocpsoft.rewrite.servlet.RewriteFilter.doFilter(RewriteFilter.java:226)
dataverse         |     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:253)
dataverse         |     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:211)
dataverse         |     at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:257)
dataverse         |     at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:166)
dataverse         |     at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:757)
dataverse         |     at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:577)
dataverse         |     at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
dataverse         |     at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:158)
dataverse         |     at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:372)
dataverse         |     at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:239)
dataverse         |     at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:520)
dataverse         |     at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:217)
dataverse         |     at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:174)
dataverse         |     at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:153)
dataverse         |     at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:196)
dataverse         |     at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:88)
dataverse         |     at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:246)
dataverse         |     at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:178)
dataverse         |     at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:118)
dataverse         |     at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:96)
dataverse         |     at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:51)
dataverse         |     at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:510)
dataverse         |     at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:82)
dataverse         |     at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:83)
dataverse         |     at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:101)
dataverse         |     at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:535)
dataverse         |     at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:515)
dataverse         |     at java.base/java.lang.Thread.run(Unknown Source)
dataverse         | Caused by: jakarta.el.ELException: /oauth2/callback.xhtml @14,74 action="#{OAuth2Page.exchangeCodeForToken()}": java.net.ConnectException: Connection refused
dataverse         |     at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:76)
dataverse         |     at com.sun.faces.application.ActionListenerImpl.getNavigationOutcome(ActionListenerImpl.java:74)
dataverse         |     ... 44 more
dataverse         | Caused by: java.net.ConnectException: Connection refused
dataverse         |     at java.base/sun.nio.ch.Net.connect0(Native Method)
dataverse         |     at java.base/sun.nio.ch.Net.connect(Unknown Source)
dataverse         |     at java.base/sun.nio.ch.Net.connect(Unknown Source)
dataverse         |     at java.base/sun.nio.ch.NioSocketImpl.connect(Unknown Source)
dataverse         |     at java.base/java.net.Socket.connect(Unknown Source)
dataverse         |     at java.base/sun.net.NetworkClient.doConnect(Unknown Source)
dataverse         |     at java.base/sun.net.www.http.HttpClient.openServer(Unknown Source)
dataverse         |     at java.base/sun.net.www.http.HttpClient.openServer(Unknown Source)
dataverse         |     at java.base/sun.net.www.http.HttpClient.<init>(Unknown Source)
dataverse         |     at java.base/sun.net.www.http.HttpClient.New(Unknown Source)
dataverse         |     at java.base/sun.net.www.http.HttpClient.New(Unknown Source)
dataverse         |     at java.base/sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(Unknown Source)
dataverse         |     at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect0(Unknown Source)
dataverse         |     at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect(Unknown Source)
dataverse         |     at java.base/sun.net.www.protocol.http.HttpURLConnection.connect(Unknown Source)
dataverse         |     at java.base/sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(Unknown Source)
dataverse         |     at java.base/sun.net.www.protocol.http.HttpURLConnection.getOutputStream(Unknown Source)
dataverse         |     at com.nimbusds.oauth2.sdk.http.HTTPRequest.toHttpURLConnection(HTTPRequest.java:939)
dataverse         |     at com.nimbusds.oauth2.sdk.http.HTTPRequest.send(HTTPRequest.java:1007)
dataverse         |     at edu.harvard.iq.dataverse.authorization.providers.oauth2.oidc.OIDCAuthProvider.getAccessToken(OIDCAuthProvider.java:268)
dataverse         |     at edu.harvard.iq.dataverse.authorization.providers.oauth2.oidc.OIDCAuthProvider.getUserRecord(OIDCAuthProvider.java:223)
dataverse         |     at edu.harvard.iq.dataverse.authorization.providers.oauth2.OAuth2LoginBackingBean.exchangeCodeForToken(OAuth2LoginBackingBean.java:103)
dataverse         |     at edu.harvard.iq.dataverse.authorization.providers.oauth2.OAuth2LoginBackingBean$Proxy$_$$_WeldSubclass.exchangeCodeForToken(Unknown Source)
dataverse         |     at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
dataverse         |     at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
dataverse         |     at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
dataverse         |     at java.base/java.lang.reflect.Method.invoke(Unknown Source)
dataverse         |     at jakarta.el.ELUtil.invokeMethod(ELUtil.java:227)
dataverse         |     at jakarta.el.BeanELResolver.invoke(BeanELResolver.java:481)
dataverse         |     at jakarta.el.CompositeELResolver.invoke(CompositeELResolver.java:198)...
Which Docker image are you using? The one called "alpha", which is 6.1 right now? Or "unstable", which is the "develop" branch?
Philip Durbin said:
Which Docker image are you using? The one called "alpha", which is 6.1 right now? Or "unstable", which is the "develop" branch?
The following is my compose.yml:
version: "2.4"
services:
dataverse:
    container_name: "dataverse"
    hostname: dataverse
    image: gdcc/dataverse:alpha
    restart: on-failure
    user: payara
    environment:
      _CT_DATAVERSE_SITEURL: "http://localhost:8080"
      DATAVERSE_DB_HOST: postgres
      DATAVERSE_DB_PASSWORD: secret
      DATAVERSE_DB_USER: dataverse
      DATAVERSE_FEATURE_API_BEARER_AUTH: "1"
      JVM_ARGS: -Ddataverse.files.storage-driver-id=file1
        -Ddataverse.files.file1.type=file
        -Ddataverse.files.file1.label=Filesystem
        -Ddataverse.files.file1.directory=${STORAGE_DIR}/store
        -Ddataverse.pid.providers=fake
        -Ddataverse.pid.default-provider=fake
        -Ddataverse.pid.fake.type=FAKE
        -Ddataverse.pid.fake.label=FakeDOIProvider
        -Ddataverse.pid.fake.authority=10.5072
        -Ddataverse.pid.fake.shoulder=FK2/
        -Ddataverse.auth.oidc.enabled=true
        -Ddataverse.auth.oidc.client-id=some_id
        -Ddataverse.auth.oidc.client-secret=some_secret
        -Ddataverse.auth.oidc.auth-server-url=http://host.docker.internal:3000/api/auth/
        -Ddataverse.auth.oidc.title=OpenID
    ports:
  - "8080:8080" # HTTP (Dataverse Application)
  - "4848:4848" # HTTP (Payara Admin Console)
  - "9009:9009" # JDWP
  - "8686:8686" # JMX
networks:
  - dataverse
depends_on:
  - postgres
  - solr
  - dv_initializer
volumes:
  - ./data/app/data:/dv
  - ./data/app/secrets:/secrets
tmpfs:
  - /dumps:mode=770,size=2052M,uid=1000,gid=1000
  - /tmp:mode=770,size=2052M,uid=1000,gid=1000
mem_limit: 2147483648 # 2 GiB
mem_reservation: 1024m
privileged: false
bootstrap:
    container_name: "bootstrap"
    image: gdcc/configbaker:alpha
    restart: "no"
    command:
  - bootstrap.sh
  - dev
  #- demo
#volumes:
#  - ./demo:/scripts/bootstrap/demo
networks:
  - dataverse
dv_initializer:
    container_name: "dv_initializer"
    image: gdcc/configbaker:alpha
    restart: "no"
    command:
  - sh
  - -c
  - "fix-fs-perms.sh dv"
volumes:
  - ./data/app/data:/dv
postgres:
    container_name: "postgres"
    hostname: postgres
    image: postgres:13
    restart: on-failure
    environment:
  - POSTGRES_USER=dataverse
  - POSTGRES_PASSWORD=secret
ports:
  - "5432:5432"
networks:
  - dataverse
volumes:
  - ./data/postgresql/data:/var/lib/postgresql/data
solr_initializer:
    container_name: "solr_initializer"
    image: gdcc/configbaker:alpha
    restart: "no"
    command:
  - sh
  - -c
  - "fix-fs-perms.sh solr && cp -a /template/* /solr-template"
volumes:
  - ./data/solr/data:/var/solr
  - ./data/solr/conf:/solr-template
solr:
    container_name: "solr"
    hostname: "solr"
    image: solr:9.3.0
    depends_on:
  - solr_initializer
restart: on-failure
ports:
  - "8983:8983"
networks:
  - dataverse
command:
  - "solr-precreate"
  - "collection1"
  - "/template"
volumes:
  - ./data/solr/data:/var/solr
  - ./data/solr/conf:/template
smtp:
    container_name: "smtp"
    hostname: "smtp"
    image: maildev/maildev:2.0.5
    restart: on-failure
    ports:
  - "25:25" # smtp server
  - "1080:1080" # web ui
environment:
  - MAILDEV_SMTP_PORT=25
  - MAILDEV_MAIL_DIRECTORY=/mail
networks:
  - dataverse
#volumes:
#  - ./data/smtp/data:/mail
tmpfs:
  - /mail:mode=770,size=128M,uid=1000,gid=1000
networks:
  dataverse:
    driver: bridge
Ok, "alpha", thanks. So 6.1.
Can you reach http://host.docker.internal:3000/api/auth from the Dataverse container? With curl or whatever?
Philip Durbin said:
Can you reach http://host.docker.internal:3000/api/auth from the Dataverse container? With curl or whatever?
But The http://host.docker.internal:3000/api/auth is my OpenID Provider's auth controller and not any specific endpoint within it.
But if I call any specific endpoint of the auth controller from within the dataverse they are called. For example the following is the result of the http://host.docker.internal:3000/api/auth/.well-known/openid-configuration endpoint:
{"issuer":"http://host.docker.internal:3000/api/auth","authorization_endpoint":"http://localhost:3000/api/auth/authorize","token_endpoint":"http://host.docker.internal:3000/api/auth/oauth-access-token","userinfo_endpoint":"http://host.docker.internal:3000/api/auth/oauth-user-profile","jwks_uri":"http://host.docker.internal:3000/api/auth/.well-known/jwks.json","response_types_supported":["code","token","id_token","code id_token"],"subject_types_supported":["public","pairwise"],"id_token_signing_alg_values_supported":["RS256","ES256","HS256"]}
I see Connection refused for  edu.harvard.iq.dataverse.authorization.providers.oauth2.oidc.OIDCAuthProvider.getAccessToken(OIDCAuthProvider.java:268. That line is specifically the point where Dataverse reaches out to the token endpoint.
Yes, but I don't know what endpoint exactly it is trying to call. I think all my configs are fine (as shown above).
Seeing this "connection refused" might either be a connection problem between Dataverse and the provider or an invalid secret thing.
If you use docker exec to open a shell in your container, can you try to reachout to the URL given in the token endpoint?
Just to make sure it's not DNS getting in our way
(I've seen very weird DNS issues lately with alpine glibc/musl images, but this is an Ubuntu based image so we should be fine with DNS resolvers)
mohsen@Mohsens-Air % docker exec -it dataverse sh
$ curl -X POST http://host.docker.internal:3000/api/auth/oauth-access-token
{"statusCode":401,"message":"Unauthorized"}$
The exec could be something like docker exec -it dataverse-1 curl "http://host.docker.internal:3000/api/auth/oauth-access-token"
The above is the call.
OK that's something! Looks like Dataverse can talk to the endpoint! Great!
Did you enable PKCE? (This is not yet very battle tested and might cause problems, checking to potentially rule it out)
I don't know how to enable or disable PKCE.
That would be JVM options
See https://guides.dataverse.org/en/latest/installation/oidc.html#provision-via-jvm-options
Just to double check: does your OIDC provider work without PKCE enabled? (Asking because some providers explicitely require you to enable PKCE)
But as you can see in the above compose.yml, I didn't set any PKCE to true.
I personally implemented the OpenID Provider and I did not implement the PKCE part, yet.
Oha! A custom OIDC provider! Fancy that!
I took some time to implement it using NestJS.
OK at this point I would either go for tcpdump + Wireshark to take a look at the packages being exchanged and/or use a Debugger to see what's going on inside
Could you give me a clue on the Debugger approach, so I can follow it.
Of course. Just a moment
Do you have a Java IDE around?
I have VSCode in my MacBook.
OK never used VSCode with a Java debugger, but I suppose there will be docs online to use it.
I will check.
You will need to checkout the v6.1 codebase https://github.com/IQSS/dataverse/tree/v6.1
Please add option as suggested in the container docs: https://guides.dataverse.org/en/latest/container/dev-usage.html#using-a-debugger
Then you can use the debugger in your IDE to set breaking points and attach it to the running JVM process
Upon trying the login, the breaking point will be triggered and the debugger should kick in
Thank you so much Oliver
No worries! I'm the one who built in any errors and bugs, so happy to help :wink:
We definetly should add a try/catch there to add some more helpful log output than just forwarding the exception :see_no_evil:
Exactly
This might be helpful: https://code.visualstudio.com/docs/java/java-debugging
I will check it.
This answer looks helpful, too, as the docs are mostly about running + debug - but we want to attach to a running JVM. https://stackoverflow.com/a/53389655
Sure
Note the https://code.visualstudio.com/docs/java/java-debugging#_hot-code-replace
You might be able to alter the running code in the JVM this way to add this try/catch block or add other specifics
Yeah
Do you know what HTTP method does dataverse use when calling the token endpoint?
POST
HTTPRequest httpRequest = new HTTPRequest(Method.POST, this.getEndpointURI()); from TokenRequest.class in Nimbus SDK
Again, a first easy step might be dumping the traffic via tcpdump and look at the packages using Wireshark
And, in the codebase, I see a bunch of isDebug() to log/not log helpful information. Can it be set using an environment variable so the isDebug() returns true, making logs to the container logs?
Sorry where do you see those? rg -i "isDebug" src/main/java gives me nothing?
OAuth20Service.class
Sounds like Scribus SDK
That is beyond our control and not used for OIDC
OIDC uses Nimbus SDK https://connect2id.com/products/nimbus-oauth-openid-connect-sdk
I put some logging middleware in my OIDC side so that I understood the problem. My token endpoint expects these four items to be received from dataverse when calling token endpoint:
client_id
client_secret
redirect_uri
code
However, the dataverse is sending the following data when calling my token endpoint:
code
redirect_uri
grant_type
scope
I updated my message.
Huh? Why are you expecting the client_id and secret?
https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest
I thought authorization code and redirect_uri wouldn't be enough as security perspective.
Implementations will likely stick to the spec and not add extras :see_no_evil:
Thanks
I see "If the Client is a Confidential Client, then it MUST authenticate to the Token Endpoint using the authentication method registered for its client_id, as described in Section 9" in there.
What kind of authentication methods do you support for confidential clients?
To be honest, I never thought of a confidential client at all in my OIDC.
You can require a confidential client to present its client_id and client_secret
With non-confidential / public clients, you can't :grinning:
You mean I can configure the dataverse so that it also sends the client_id and secret when calling token endpoint?
Actually I think this should already happen
As in "supposed to happen"
From "new TokenRequest(this.idpMetadata.getTokenEndpointURI(), this.clientAuth,..." it's already clear that the request has the confidential information (this.clientAuth)
This is the whole body of the dataverse request towards my token endpoint:
Body: {"code":"123456789","redirect_uri":"http://some_IP:8080/oauth2/callback.xhtml","grant_type":"authorization_code","scope":"openid email profile"}
Sorry I forgot: have you configured the confidential secrets?
From what I see in the code, this _should_ work already and include these details.
Oh oh oh wait
We're using "ClientSecretBasic".
That one sets the secrets as Authorization HTTP header
I only set the following in domain.xml
<jvm-options>-Ddataverse.auth.oidc.enabled=true</jvm-options>
        <jvm-options>-Ddataverse.auth.oidc.client-id=some_id</jvm-options>
        <jvm-options>-Ddataverse.auth.oidc.client-secret=some_secret</jvm-options>
        <jvm-options>-Ddataverse.auth.oidc.auth-server-url=https://my.domain.com/api/auth/</jvm-options>
        <jvm-options>-Ddataverse.auth.oidc.title=OpenID</jvm-options>
Could you check the headers if they are present?
Yes
Headers: {"x-real-ip":"some_ip","x-forwarded-for":"some_ip, some_ip","host":"my.example.com","x-nginx-proxy":"true","connection":"close","content-length":"172","authorization":"Basic YzdjZGE5NjgtNWM3Mi00NTg1LTlmOGEtNWQ4NjBlNzFhNzE2OmJjM2RhMWNjMTY2MGIxMTRiNDA4ZThiNTJmYzM0OThmZDJlMDI4ZDg5MmJkMGMyMTc5MzhmZjBlNmIxMzNkYjU=","content-type":"application/x-www-form-urlencoded; charset=UTF-8","user-agent":"Java/17.0.10","accept":"text/html, image/gif, image/jpeg, *; q=.2, /; q=.2"}
Ha there we go!
Quoting from the OIDC spec:
"client_secret_basic
    Clients that have received a client_secret value from the Authorization Server authenticate with the Authorization Server in accordance with Section 2.3.1 of OAuth 2.0 [RFC6749] using the HTTP Basic authentication scheme. "
https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication
So you could say your confidential clients are only allowed to use the post method (quote: "including the Client Credentials in the request body"), which would indeed need an extension of Dataverse to make people select the method to be used
Doing that should be simple: this constructor for the provider needs to be changed:
public OIDCAuthProvider(String aClientId, String aClientSecret, String issuerEndpointURL,
                            boolean pkceEnabled, String pkceMethod) throws AuthorizationSetupException {
        this.clientSecret = aClientSecret; // nedded for state creation
        this.clientAuth = new ClientSecretBasic(new ClientID(aClientId), new Secret(aClientSecret));
This would need a distinction which method to use and act accordingly (different classes)
Was I in the wrong path when implementing my OIDC that now need to modify the dataverse's codebase, OR this is usual?
Yes and no
It seems to me you have only implemented one option of client auth methods
The HTTP Basic Auth theme is pretty common as far as I know. I'm not aware of a provider not supporting it.
These are design decisions you have to make.
So updating my OIDC provider to support it would make more sense, compared to touching the dataverse side.
We would be glad to accept a pull request extending Dataverse. There even are integration tests in place using Keycloak, so it would be easy to get you started.
That said, if you are looking for quick results and want to be more compatible with clients, it probably is a good idea to add HTTP basic auth support to your provider
I have to discuss with my team lead and decide what to do. Anyway, thank you so much for your help.
Mohsen Jafari has marked this topic as resolved.
No worries! That's what we're here for. I am going ahead and mark this topic as resolved. If you opt for developing a patch for Dataverse, please create a new topic over at the #dev stream, thx!
You beat me to it :smile_cat:
Last updated: Oct 30 2025 at 06:21 UTC