{"id":16237277,"url":"https://github.com/christopherpickering/remix-auth-saml","last_synced_at":"2025-03-19T15:31:11.294Z","repository":{"id":163666586,"uuid":"639101197","full_name":"christopherpickering/remix-auth-saml","owner":"christopherpickering","description":"A SamlStrategy for Remix Auth.","archived":false,"fork":false,"pushed_at":"2024-05-19T13:25:48.000Z","size":1331,"stargazers_count":1,"open_issues_count":5,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-05-19T14:34:21.981Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/christopherpickering.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"christopherpickering","custom":"https://venmo.com/code?user_id=3118520086822912596\u0026created=1655827155"}},"created_at":"2023-05-10T19:03:13.000Z","updated_at":"2024-06-02T11:57:33.748Z","dependencies_parsed_at":null,"dependency_job_id":"de3e80e4-db8f-4f52-99f8-7df4fe1c8b1c","html_url":"https://github.com/christopherpickering/remix-auth-saml","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":"sergiodxa/remix-auth-strategy-template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/christopherpickering%2Fremix-auth-saml","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/christopherpickering%2Fremix-auth-saml/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/christopherpickering%2Fremix-auth-saml/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/christopherpickering%2Fremix-auth-saml/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/christopherpickering","download_url":"https://codeload.github.com/christopherpickering/remix-auth-saml/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244003478,"owners_count":20382194,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-10T13:35:09.508Z","updated_at":"2025-03-19T15:31:10.929Z","avatar_url":"https://github.com/christopherpickering.png","language":"TypeScript","funding_links":["https://github.com/sponsors/christopherpickering","https://venmo.com/code?user_id=3118520086822912596\u0026created=1655827155"],"categories":[],"sub_categories":[],"readme":"# Remix Auth Saml Strategy Template\n\nUse this strategy to add SAML single sign on to your remix site.\n\n## Supported runtimes\n\n| Runtime    | Has Support |\n| ---------- | ----------- |\n| Node.js    | ✅          |\n| Cloudflare | ✅          |\n\n## Install\n\nInstall the package.\n\n```bash\nnpm i remix-auth-saml\n```\n\n## Setup\n\nInstall a validator as specified in the samlify readme https://github.com/tngan/samlify/tree/master\n\n### `services/auth.server.ts`\n\n```ts\nimport { Authenticator } from \"remix-auth\";\nimport { sessionStorage } from \"~/services/session.server\";\nimport { SamlStrategy } from \"remix-auth-saml\";\n\n// install and import an xml validator as directed here: https://github.com/tngan/samlify/tree/master\nimport * as validator from \"@authenio/samlify-node-xmllint\";\n\nexport let authenticator = new Authenticator\u003cany\u003e(sessionStorage);\n\nlet samlStrategy = new SamlStrategy(\n  {\n    validator,\n    authURL: \"http://localhost:3000\",\n    callbackURL: \"http://localhost:3000/auth/saml/callback\",\n    idpMetadataURL: \"http://localhost:7000/metadata\",\n    spAuthnRequestSigned: false,\n    spWantAssertionSigned: false,\n    spWantMessageSigned: false,\n    spWantLogoutRequestSigned: false,\n    spWantLogoutResponseSigned: false,\n    spIsAssertionEncrypted: false,\n    // optional\n    privateKey: \"./test/saml-idp/idp-private-key.pem\",\n    // optional\n    privateKeyPass: \"\",\n    // optional\n    encPrivateKey: \"./test/saml-idp/idp-private-key.pem\",\n    // optional\n    privateKey: \"./test/saml-idp/idp-private-key.pem\",\n    // optional\n    signingCert: \"./test/saml-idp/idp-private-key.crt\",\n    // optional\n    encryptCert: \"./test/saml-idp/idp-private-key.crt\",\n  },\n  async ({ extract, data }) =\u003e {\n    console.log(\"profile\", extract);\n    // data is the raw response from the idp\n    // this could be passed into a backend for decryption\n    // if you need to verify authentication in the backend.\n    // if remix is the backend, the you can use the\n    // extract directly\n    console.log(\"data\", data);\n    return false;\n  },\n);\n\nexport let metadata = samlStrategy.metadata();\n\nauthenticator.use(samlStrategy);\n```\n\n### `routes/auth.saml.tsx`\n\n```ts\nimport type { ActionFunction, LoaderFunction } from \"@remix-run/node\";\nimport { authenticator } from \"~/services/auth.server\";\n\nexport let action: ActionFunction = ({ request }) =\u003e login(request);\nexport let loader: LoaderFunction = ({ request }) =\u003e login(request);\n\nasync function login(request: Request) {\n  return authenticator.authenticate(\"saml\", request);\n}\n```\n\n### `routes/auth.saml.callback.tsx`\n\n```ts\nimport type { ActionFunction, LoaderFunction } from \"@remix-run/node\";\nimport { authenticator } from \"~/services/auth.server\";\n\nexport let action: ActionFunction = ({ request }) =\u003e login(request);\nexport let loader: LoaderFunction = ({ request }) =\u003e login(request);\n\nasync function login(request: Request) {\n  let successRedirect = \"/\";\n\n  try {\n    // if relay state was set we can redirect to it.\n    const newRequest = request.clone();\n    const formData = await newRequest.formData();\n    const body = Object.fromEntries(formData);\n    successRedirect = (formData.get(\"RelayState\") || \"/\").toString();\n  } catch (e) {}\n\n  // call authenticate to complete the login and set returnTo as the successRedirect\n  return authenticator.authenticate(\"saml\", request, {\n    successRedirect,\n    failureRedirect: \"/unauthenticated\",\n  });\n}\n```\n\n### Usage in protected routes\n\n```ts\nimport { authenticator } from \"~/services/auth.server\";\nimport { json } from \"@remix-run/node\";\nimport type { LoaderArgs } from \"@remix-run/node\";\n\nexport async function loader({ request }: LoaderArgs) {\n  // to redirect to login if not authed\n  // The current url can be passed passed to the ipd as a RelayState when\n  // a user accesses a protected page without being logged in.\n  // They will run through the saml process and then be redirected to\n  // the same page.\n\n  let user = await authenticator.isAuthenticated(request, {\n    failureRedirect: `/auth/saml/?returnTo=${encodeURI(request.url)}`,\n\n    // or to go back to the root `/`\n    //failureRedirect: \"/auth/saml/\",\n  });\n  return json({ user });\n}\n\nexport default function Login() {\n  return \u003c\u003eprotected page!\u003c/\u003e;\n}\n```\n\n### Access SP Metadata `routes/metadata[.]xml.tsx`\n\nYou may need access to the `sp` metadata to keep in sync with your `ipd` automatically.\n\n```ts\nimport { metadata } from \"~/services/auth.server\";\n\nexport const loader = async () =\u003e {\n  return new Response(metadata, {\n    status: 200,\n    headers: {\n      \"Content-Type\": \"text/xml\",\n    },\n  });\n};\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchristopherpickering%2Fremix-auth-saml","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchristopherpickering%2Fremix-auth-saml","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchristopherpickering%2Fremix-auth-saml/lists"}