Skip to content

Authentication

En Rong edited this page May 13, 2024 · 1 revision

Authentication Providers

The Source Academy application supports authentication providers with the following protocols:

  • OAuth Code Flow
    • OAuth 2.0
    • OIDC
    • CAS (to be merged)
  • SAML

Feel free to take a look at the auth providers directory here to see a list of authentication providers that have been integrated.

Setup for local development

OAuth Code Flow

  • Authentication providers which follow the OAuth code flow can easily be configured with the instructions found in dev.secrets.exs.
  • New authentication providers can be added easily by configuring a new Provider in the backend by referring to existing examples.

SAML Flow

NOTE: The SAML flow does not play well with modern day SPAs like React. Some workarounds are necessary to integrate SAML SSO providers with SPAs. The current approach is as follows:

  • After the SAML assertion is validated by the Assertion Consumer Service (ACS) endpoint in the backend, we redirect to a separate endpoint on the backend /auth/saml_redirect to generate the JWT tokens that will be used for subsequent communication between the frontend and backend.
  • After the JWT tokens are generated, we redirect once more to the frontend client with the JWTs in a secure cookie.
  • This is as opposed to the OAuth code flow where the frontend makes a POST request with the authorization code from the authentication provider to exchange for a set of JWT tokens.
  • Other methods of integrating SAML and SPAs exist, such as adding an intermediary server to handle the SAML exchange, while retaining the OAuth code flow interface with the frontend client.
  • SAML Identity Providers (IdP) for local development are less readily available online since they require the SAML public certificate from the Service Provider (SP, i.e. our backend).
  • Nevertheless, we can quickly configure our own local SAML IdP with the following instructions to test ou SAML auth flow.

Setup a local SAML IdP for testing SAML auth flow locally

The following instructions were referenced from https://github.com/handnot2/samly_simplesaml, with some modifications to suit our configuration.

  1. Pull the repository here.

  2. If on Apple Silicon, modify the Dockerfile with the following line to run the emulation of x86-64 architecture instead. This is necessary so that the setup container does not fail silently.

    FROM --platform=linux/amd64 php:7.1-fpm-alpine3.8
    
  3. Build the Docker image.

    ./build.sh
  4. Add the following to the /etc/hosts file on your local computer.

    # Inside /etc/hosts
    127.0.0.1 api.cadet.ap # for the backend SP
    127.0.0.1 nusstu.samly # for the SimpleSAML IdP
  5. Copy the self-signed cert used for signed SAML requests to the setup/sp directory. Note that this is different from the SSL cert for the backend SP.

    • See samly-howto for more details.
      • In the cadet backend directory, run mix phx.gen.cert --output priv/cert/backend_sp nusstu.samly
  6. Configure SimpleSAML via a simple docker-compose wrapper

    #!/bin/sh
    # Save this as idp1-compose.sh
    
    export USE_SUBDOMAIN=0
    export TIMEZONE=Asia/Singapore # <<- change this if needed
    
    export SP_DOMAIN=api.cadet.ap # <<- change this, this is same as SP HOST in path_segment model
    export SP_PORT=4443 # <<- change this
    export SP_ENTITY_ID=sa-backend # <<- change this - should match what is in samly config
    export SP_CERT_FILE=backend_sp.pem # <<- change this - this should be present in ./setup/sp folder
    
    export PSWD=password # <<- change this - password for all demo users in ./setup/templates/params.tpl
    
    export IDP_ID=nusstu # <<- change this
    export IDP_HOST=${IDP_ID}.samly
    export IDP_PORT=9091 # <<- change this
    
    sudo -E ./compose.sh $*
  7. Modify the SAML attributes in setup/templates to suit the needs of our backend application

    # In authsources.php.tpl modify the following lines
    
    'samaccountname' => array('{{ $user.samaccountname }}'),
    # 'first_name' => array('{{ $user.first_name }}'),
    # 'last_name' => array('{{ $user.last_name }}'),
    # In params.tpl modify the following lines
    
    # first_name: Fred
    # last_name: Stone
    samaccountname: Fred Stone
    
    # first_name: Wilma
    # last_name: Stone
    samaccountname: Wilma Stone
    
    # first_name: Dino
    # last_name: Stone
    samaccountname: Dino Stone
    
  8. Start the IdP containers (the repeated commands are as per Samly SimpleSAML README)

    ./idp1-compose.sh up -d
    ./idp1-compose.sh down
    ./idp1-compose.sh up -d
  9. Double check the configuration

    ./idp1-compose.sh info
  10. Visit https://nusstu.samly:9091/simplesaml

    • Login as admin with the configured password above
    • Check the Federation tab, you should see the SAML service provider
    • Copy the metadata.xml to the cadet backend directory under priv/metadata/simplesaml_metadata.xml

Setup the Elixir backend for local SAML auth flow

  1. Generate a self-signed SSL cert for the backend

    mix phx.gen.cert --output priv/cert/cadet_local api.cadet.ap
  2. Generate a self-signed SSL cert for SAML requests, as per the instructions above. Remember to copy it to the SimpleSAML setup/sp directory before starting the SimpleSAML container.

    mix phx.gen.cert --output priv/cert/backend_sp nusstu.samly
  3. Set the /etc/hosts file if you have yet to do so, as per the instructions above.

  4. Set the priv/metadata/simplesaml_metadata.xml file if you have yet to do so, as per the instructions above.

  5. Configure dev.secrets.exs

    • Under identity_providers add
      "student_saml" =>
        {Cadet.Auth.Providers.SAML,
        %{
            assertion_extractor: Cadet.Auth.Providers.NusstuAssertionExtractor,
            client_redirect_url: "http://cadet.ap:8000/login/callback"
        }},
      
    • Configure Samly
      config :samly, Samly.Provider,
        idp_id_from: :path_segment,
          service_providers: [
            %{
              id: "source-academy-backend",
              entity_id: "sa-backend",
              certfile: "priv/cert/backend_sp.pem",
              keyfile: "priv/cert/backend_sp_key.pem"
            }
          ],
          identity_providers: [
            %{
              id: "nusstu",
              sp_id: "source-academy-backend",
              base_url: "https://api.cadet.ap:4443/sso",
              metadata_file: "priv/metadata/simplesaml_metadata.xml"
            }
          ]
      
    • Configure the backend to use HTTPS
      config :cadet, CadetWeb.Endpoint,
        https: [
          port: 4443,
          cipher_suite: :strong,
          certfile: "priv/cert/cadet_local.pem",
          keyfile: "priv/cert/cadet_local_key.pem"
        ],
        debug_errors: true,
        code_reloader: true,
        check_origin: false
      
  6. In lib/cadet_web/controllers/auth_controller.ex, make the following modification to disable secure cookies (for local development only, since the frontend client is on http)

    conn
    |> put_resp_cookie("jwts", encoded_tokens,
      domain: URI.new!(client_redirect_url).host,
      http_only: false,
      secure: false # Added this line
    
  7. Write a backend start-up script

    #!/bin/sh
    # Save to start-backend.sh
    
    HOST="${HOST:-api.cadet.ap}" \
    PORT="${PORT:-4443}" \
    MIX_ENV=dev \
      iex -S mix phx.server
  8. chmod +x and run the start-up script

  9. You should be able to access the backend at https://api.cadet.ap:4443

Setup the React frontend for local SAML auth flow

  1. Add or update the following values in the .env file.
    REACT_APP_SAML_PROVIDER1=student_saml
    REACT_APP_SAML_PROVIDER1_NAME=NUS Student SAML
    REACT_APP_SAML_PROVIDER1_ENDPOINT=https://api.cadet.ap:4443/sso/auth/signin/nusstu
    HOST=cadet.ap
    REACT_APP_BACKEND_URL=https://api.cadet.ap:4443
    
  2. Add the following to the /etc/hosts file on your local computer.
    127.0.0.1 cadet.ap
    
  3. Run yarn start to start the frontend.
  4. Access the frontend at http://cadet.ap:8000.
  5. If everything was configured correctly, you should be able to log in via the SAML flow with the SimpleSAML credentials.