Login with EntraID (Azure)

Well, How do i need to prepare Specify itself (how to test it)?, how is the link between specify and an idp like entra. Do i need to create same user by hand? Will it be populated? Group restrictions, etc. But for Starting, a Bird view of implementation an “quickstart” would be cool. Everybody knows how OIDC etc. works more or less, but implemetation/integration in a System varies a lot.

1 Like

Hello All, thank to @vinayakjha we have now a working Login with Entra ID. Problem was basically a missing setting. The counterpart of

SECURE_PROXY_SSL_HEADER = (‘HTTP_X_FORWARDED_PROTO’, ‘https’)

was missing in nginx config.

proxy_set_header X-Forwarded-Proto $scheme;

And it worked like a charm!

Edit: Just let me know if you struggle with the Entra-App Part

1 Like

Hello @dfernandez,
Not sure where this line should go in the nginx.conf file:

Could you share a copy of your file?
Thanks

Hy @Heryk

sure, my config file looks like this atm:

server {
    listen 443 ssl;
    listen 80;
    http2 on;

    server_name specify.yourdomain.com;

    ssl_certificate /etc/nginx/certs/fullchain.pem;
    ssl_certificate_key /etc/nginx/certs/privkey.pem;

    ssl_session_cache shared:le_nginx_SSL:10m;

    ssl_session_timeout 1440m;
    ssl_session_tickets off;

    # curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam
    ssl_dhparam /etc/nginx/certs/dhparam;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
    ssl_prefer_server_ciphers on;

    root /usr/share/nginx;

    # serve static files directly
    location /static/ {
        root /volumes;
        rewrite ^/static/config/(.*)$ /specify6/config/$1 break;
        rewrite ^/static/depository/(.*)$ /static-files/depository/$1 break;
        rewrite ^/static/(.*)$ /static-files/frontend-static/$1 break;
    }

    # proxy these urls to the asset server
    location ~ ^/(fileget|fileupload|filedelete|getmetadata|testkey|web_asset_store.xml) {
        resolver 127.0.0.11 valid=30s;
        client_max_body_size 5000M;
        set $backend "http://asset-server:8080";
        proxy_pass $backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    # proxy everything else to specify 7
    location / {
        resolver 127.0.0.11 valid=30s;
        client_max_body_size 5000M;
        set $backend "http://specify7:8000";
        proxy_pass $backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
       proxy_set_header X-Forwarded-Proto $scheme;
        }
        #access_log /etc/nginx/certs/logs/access.log;
        #error_log /etc/nginx/certs/logs/error.log;
}

Hope that helps

1 Like

Hi,
I keep on getting callback error message pointing to “localhost” (http://localhost/accounts/oic_callback/) and not my domain name. How can I get the callback to use my domain name and not localhost? I must be missing something in the config. Any idea of what I’m doing wrong?

I also noticed that my callback is using “http” rather than “https”! Should we keep using docker image “specifyconsortium/specify7-service:sso-https” or “specifyconsortium/specify7-service:v7”? Will “specifyconsortium/specify7-service:v7” be updated to use “https” with SSO?

Examples:

  1. With Docker image “specifyconsortium/specify7-service:v7” I get a “http” callback…
    image

  2. With Docker image “specifyconsortium/specify7-service:sso-https” I get a “https” callback instead…

Hi, did you try adding this line to your nginx file proxy_set_header X-Forwarded-Proto $scheme;
like this:

# proxy everything else to specify 7
    location / {
        client_max_body_size 400M;
        client_body_buffer_size 400M;
        client_body_timeout 120;
        resolver 127.0.0.11 valid=30s;
        set $backend "http://specify7:8000";
        proxy_pass $backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme; # add this line
    }

If that doesn’t work, maybe try hard-coding it to https like this proxy_set_header X-Forwarded-Proto https;

Also, add the line SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') to the secifyweb/settings/specify_settings.py file.

Another problem might be that port 443 isn’t open.

Make sure the Django and nginx settings are updated with docker compose down; docker compose up --build -d;

After reading the error page Error AADSTS50011 the redirect URI does not match the redirect URIs configured for the application - Azure | Microsoft Learn, it seems the URL defined in the your Azure portal dones’t match the requested URL. Check the full request header in the login.microsoftonline.com/ sign in page to get the requested redirect URI for Specify, and then add that to your Azure portal authorization accepted URIs. Maybe try adding both https://localhost/accounts/oic_callback/ and http://localhost/accounts/oic_callback/ in the Azure portal. Just make sure to remove those for production.

Let me know if any of that works for you.

1 Like

Hello @Heryk

Just use the regular images but just destroy the images and redeploy instead of just restarting (just to be sure). I noticed that is the only way to get the edited config applied right.

1 Like

Hello @alec.white and @dfernandez,

I checked and I do have the following line in my nginx file:

proxy_set_header X-Forwarded-Proto $scheme;

The following line is also present at the end of my specify_settings.py file:

SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

I deleted all my images and containers to make sure I started from scratch and executed several tests and got the same errors. The v7 tagged specify7-service image doesn’t use “https” in the callback but the sso-https tagged does. In addition, both v7 and sso-https tagged images redirect me to the incorrect “localhost” URL (localhost/accounts/oic_callback). They don’t use my domain name.

Is there a way to hardcode the callback URL so that I don’t get a redirect to “localhost”?

I also tried hard coding “https” in the nginx configuration file as suggested:

proxy_set_header X-Forwarded-Proto https;

However, instead of being redirected to the Microsoft user authentication login page as before, I now get an error message from the /accounts/login page indicating that the CSRF verification failed. Could you confirm that I have the correct syntax for hard coding “https”?

image

PS. Here’s a little more context on my setup… For security concerns, Docker is prohibited on our servers. So, I’m using Podman with Pods to run Specify-7 instead of just using it with Docker and Compose. I have Apache setup on a RHEL server that reverse proxies requests to a Podman Pod containing all the Specify 7 containers (SP-6, NGINX, Asset server, Redis, SP-7, SP-7 Worker and Report runner). Except for my current issues with SSO (Single Sign-On), everything works just fine.

Thanks for your help
Héryk

Great news! I’m almost there!

As I mentioned, I have Apache set up on a RHEL server that reverse proxies requests to a Podman Pod containing all the Specify 7 containers.

To finally resolve the issue with the oic_callback pointing to “localhost” instead of my domain name, I had to make changes to my Apache configuration. The two main modifications were:

RequestHeader set X-Forwarded-Proto "https"
ProxyPreserveHost On 

Now, EntraID redirects to Specify! :slight_smile: However, just like @dfernandez experienced, I’m encountering a KeyError exception from Specify on callback:

{"exception": "KeyError", "message": "id_token", "data": "None", "traceback": "Traceback (most recent call last):\n File \"/opt/specify7/ve/lib/python3.8/site-packages/django/core/handlers/base.py\", line 181, in _get_response\n response = wrapped_callback(request, *callback_args, **callback_kwargs)\n File \"/opt/specify7/ve/lib/python3.8/site-packages/django/views/decorators/http.py\", line 40, in inner\n return func(request, *args, **kwargs)\n File \"/opt/specify7/specifyweb/accounts/views.py\", line 183, in oic_callback\n id_token = get_token.json()['id_token']\nKeyError: 'id_token'\n"}

By the way, I have added SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') to the specifyweb/settings/specify_settings.py file and proxy_set_header X-Forwarded-Proto $scheme in my nginx.conf file.

Any suggestions would be greatly appreciated. :wink:

Wow!!! It worked!

I just deleted all my containers, and the configuration modifications were successful! I’m now using EntraID to authenticate! Awesome! :smiley:

P.S. I believe having official documentation would be incredibly helpful for future users who want to implement SSO with Specify 7.

1 Like