How to login to server with user credentials without using speckle frontend

Hi community,

We have a requirement to login user(actually first user > server:admin) to speckle server on our .net Core Server. We are dockerizing our server and was hoping to read admin credentials from environment file and login(first login)/register to speckle server while we are initializing our .net core server.

Is there a way to do that with some endpoint like in the v1(UserLoginAsync)?

Cheers

Iā€™ll ask a bit of an extra question: why do you need to programatically login? Is it to create an access token, or start scaffolding extra info?

In v2, you cannot simply ā€œloginā€ - you (as a speckle app) need to request authorisation from the user to act on his behalf, and then youā€™ll get a token. Thereā€™s a difference as well between first party apps (pre-registered) and third-part apps (dynamically registered) - the scopes that are available to them are different (less in the case of the latter).

I suspect, (pending your answer of course :sweat_smile: - it might change things completely!) that this is pretty much backend-to-backend communication. If thatā€™s the case, the simplest way might be to write some post-install scripts on your speckle server deployment to do what is needed (e.g., pre-register your server as an app, or even insert users, tokens, etc. in the databaseā€¦).

1 Like

Hi Dimitrie,

We need programmatically login to be able to subscribe specific streams. If our server down and up we have to subscribe streams again. Since we do not have any Account to initialize speckle gql client at this point, we wanted to use admin credentials to do that.

I have a question regarding implementation of pre-registered apps. Is that something like speckle main app(frontend) or GQL explorer?

Ah, but then this is exactly what webhooks and the recent events pipeline is for! Check out

and

https://speckle.guide/dev/server-webhooks.html

If your server is down, or donā€™t want to rely on webhooks either, you could even just poll every minute or five or xxx the activity stream of the streams youā€™re interested in.

Hi Dimitrie,

Thank you for the information! Unfortunetaly, webhooks does not help us also, because we are doing changes in some streams when we notified by subscription or webhooks. We do not need the register webhook again on restart which solves issue on startup. However, we will need the Account when we notified by webhook/subscription to do changes on streams. :pensive:

Okay, hereā€™s what you need to do then - of the top of my head:

  • pre-register your server application on the speckle server (make sure youā€™re setting scopes to ā€˜allā€™ if the intention is for full access). You can do so by by modifying this file: speckle-server/defaultApps.js at main Ā· specklesystems/speckle-server Ā· GitHub
    Alternatively, this can be done with a script that just inserts the correct data in the database, so you donā€™t need to modify the source. Youā€™ll need to run this script post-install, so it does mean some changes in the docker file.

  • register your first user via the standard auth flow:

    • youā€™ll need to send a post request to /auth/register; you will get an access code. Include your appId and a randomly generated challenge.
    • exchange the access code for a token and refresh token via /auth/token, including your appId, appSecret and the challenge from the previous step.
    • profit! you now have an admin level api token :sunglasses:

This sounds all quite confusing - i know! To help understand the flow, you can have a look at the handshakes that the frontend application is going through with the server, as itā€™s the same flow.

Mind you, I havenā€™t followed this along myself, so if I have time iā€™ll see if I can cook something up if you donā€™t manage to get it working!

2 Likes

Thank you for the detailed steps Dimitrie! We will try and share the results here :slight_smile:

1 Like

Hello Dimitrie,
I work in the same team as Baris and I tried your solution. The link ā€œhttp://localhost/auth/registerā€ doesnt seem to work for me. This gets me an error: ā€œCannot POST /auth/registerā€. Instead I tried using /authn/register which seems to work but than I get an 405 error. I sent the request using Postman as a POST-Request with Headers: Content-Type: application/json and Body: appId: ā€œ{myAppId}ā€ challenge: ā€œrandomChallengeā€ appSecret: ā€œ{myAppSecret}ā€. This Postrequest returns a 405 response. The App and its AppId and AppSecret is registered in my DB under server_apps. Do you have an idea why Iā€™m not allowed to post to this url? Thanks in advance :slight_smile:

Hi @mgerhardt! Before we jump deeper, you had the port on your localhost url (ie, ā€œhttp://localhost:3000/auth/register)?

Yes, itā€™s http://localhost:80/authn/register to be precise

Hey! Gotcha - itā€™s an easy fix. Those are frontend routes. The API is at server_url/auth/register - thatā€™s where you need to send your POST requests.

Thankfully I double checked. Theyā€™re actually at:

We know this is bad, and itā€™s within our roadmap (maybe @cristi will flag an issue if this is not flagged already) we want to move all api routes to a slash api prefix.

Hello, first of all thank you for the quick replies. But this one gets me a little bit confused. Sending a request to this route always gets me an ā€œInvalid request: no challenge detected.ā€ error, even though the challenge is in the request body. I also made sure there is no typo. And looking at the code, it seems like this endpoint also expects the request to have an user and a password for registration. The route that Iā€™m using is ā€œhttp://localhost:80/auth/local/registerā€. I think that I am really close now, just missing a little bit :slight_smile:

1 Like

Heya! Not sure if this is the issue, but have you tried passing in the challenge as a url param rather than setting on the body?

2 Likes

Hello izzy :slight_smile: that seems to work so far. I can register new users through this route. The users are added to the database and I can see them. The only problem remaining is, that I dont get an accessCode in the response after registering or logging in. Instead I just get a new error message saying: " Weā€™re sorry but Speckle doesnā€™t work properly without JavaScript enabled. Please enable it to continue." Here is a screenshot of postman.

By the way, your github links are really usefull giving me more insight into how speckle works! :+1:

1 Like

heya!

if you look down a bit further form the snippet I linked, you can see the access code will actually be in the redirect URL

you wonā€™t get it in the preview in postman which will just be a webpage. not sure where you look in postman to find the redirect url (I donā€™t use postman very much tbh), but i know you can just turn off the auto redirect like this (second option in the settings toggled off):

note the response content shows you the redirect url with the access code on there!

1 Like

Ok, so weā€™re getting there! I got my accessCode and now Iā€™m only missing the token. I tried the same code you wrote in your github seeder but I still get an 401 error saying ā€œInvalid request: application id does not match.ā€ I also get the same error when using postman. Here is a screenshot:


I actually do get an accessToken when I set appId and appSecret to spklwebapp but I want it to work for my appId and secret :).

Also here is another small screenshot from my database to show that that appId actually matches:

Hello Dimitrie , how can you give appId in /auth/local/register ? there is no column name called appId.

@Mark_Eskander, I iā€™m wrong in my answer there, register routes donā€™t need appids. @mgerhardt i think your request borks as youā€™re using the wrong encoding (form encoding). Havenā€™t used postman in ages, but if I recall correctly, you need to use ā€œrawā€ and paste in a json formatted string.

If you give me some time, Iā€™ll try and write a quick .net console app that goes through the motions.

2 Likes

that sounds great. Here is a codesnippet that i wrote to do the steps. Maybe this will ease your work a little bit :stuck_out_tongue:

    public async void testSpeckleLogin(string serverUrl) 
    {
        string challange = Guid.NewGuid().ToString();
        var APP_SPECKLE_ID = "SOME_ID";
        var APP_SPECKLE_SECRET = "SOME_SECRET";
        var tokenUrl = "http://localhost:80/auth/local/login?challenge=testchallenge";

        using (var _httpClient = new HttpClient())
        {
            var jsonObject= new
            {
                password = "supersecretpassword",
                name = "myname",
                email = "myname.mylastname@mycompany.com"
            };
            string json = JsonConvert.SerializeObject(jsonObject);
            StringContent data = new StringContent(json, Encoding.UTF8, "application/json");
            var response = await _httpClient.PostAsync(tokenUrl, data);
            string query = response.RequestMessage.RequestUri.Query;
            string access_code = query.Split("access_code=")[1];

            //exchangeAccessToken
            var requestBody = new
            {
                accessCode = access_code,
                appId = APP_SPECKLE_ID,
                appSecret = APP_SPECKLE_SECRET,
                challenge = "testchallenge"
            };
            string jsonRequestBody = JsonConvert.SerializeObject(requestBody);
            StringContent accessTokenData = new StringContent(jsonRequestBody, Encoding.UTF8, "application/json");
            string accessTokenUrl = "http://localhost/auth/token";
            var newResponse = await _httpClient.PostAsync(accessTokenUrl, accessTokenData);

            _httpClient.Dispose();
   }
3 Likes

Hey izzy, do you maybe have an idea why I get an accessToken with appId and appSecret set to ā€œspklwebappā€ like you did in your seeder but not with my custom id and secret? Maybe I have to configure something in my database that I have missed to make this work?