-
Notifications
You must be signed in to change notification settings - Fork 50
Authentication
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.
- 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.
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.
The following instructions were referenced from https://github.com/handnot2/samly_simplesaml, with some modifications to suit our configuration.
-
Pull the repository here.
-
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
-
Build the Docker image.
./build.sh
-
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
-
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
- In the cadet backend directory, run
- See samly-howto for more details.
-
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 $*
-
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
-
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
-
Double check the configuration
./idp1-compose.sh info
-
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 underpriv/metadata/simplesaml_metadata.xml
-
Generate a self-signed SSL cert for the backend
mix phx.gen.cert --output priv/cert/cadet_local api.cadet.ap
-
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
-
Set the
/etc/hosts
file if you have yet to do so, as per the instructions above. -
Set the
priv/metadata/simplesaml_metadata.xml
file if you have yet to do so, as per the instructions above. -
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
- Under
-
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 onhttp
)conn |> put_resp_cookie("jwts", encoded_tokens, domain: URI.new!(client_redirect_url).host, http_only: false, secure: false # Added this line
-
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
-
chmod +x and run the start-up script
-
You should be able to access the backend at
https://api.cadet.ap:4443
- 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
- Add the following to the
/etc/hosts
file on your local computer.127.0.0.1 cadet.ap
- Run
yarn start
to start the frontend. - Access the frontend at
http://cadet.ap:8000
. - If everything was configured correctly, you should be able to log in via the SAML flow with the SimpleSAML credentials.