CINXE.COM

Authentication - Turn-key research data management repository

<!doctype html> <html lang="en" class="no-js"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <link rel="canonical" href="https://inveniordm.docs.cern.ch/customize/authentication/"> <link rel="prev" href="../look-and-feel/font/"> <link rel="next" href="../emails/"> <link rel="icon" href="../../images/favicon.svg"> <meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.5.40"> <title>Authentication - Turn-key research data management repository</title> <link rel="stylesheet" href="../../assets/stylesheets/main.8c3ca2c6.min.css"> <link rel="stylesheet" href="../../assets/stylesheets/palette.06af60db.min.css"> <link rel="stylesheet" href="../../stylesheets/extra.css"> <script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script> </head> <body dir="ltr" data-md-color-scheme="default" data-md-color-primary="custom" data-md-color-accent="custom"> <input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off"> <input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off"> <label class="md-overlay" for="__drawer"></label> <div data-md-component="skip"> <a href="#authentication" class="md-skip"> Skip to content </a> </div> <div data-md-component="announce"> </div> <header class="md-header" data-md-component="header"> <nav class="md-header__inner md-grid" aria-label="Header"> <a href="../.." title="Turn-key research data management repository" class="md-header__button md-logo" aria-label="Turn-key research data management repository" data-md-component="logo"> <img src="../../images/logo-rdm.png" alt="logo"> </a> <label class="md-header__button md-icon" for="__drawer"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class="md-header__title" data-md-component="header-title"> <div class="md-header__ellipsis"> <div class="md-header__topic"> <span class="md-ellipsis"> Turn-key research data management repository </span> </div> <div class="md-header__topic" data-md-component="header-topic"> <span class="md-ellipsis"> Authentication </span> </div> </div> </div> <form class="md-header__option" data-md-component="palette"> <input class="md-option" data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme="default" data-md-color-primary="custom" data-md-color-accent="custom" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_0"> <label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_1" hidden> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class="md-option" data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme="slate" data-md-color-primary="black" data-md-color-accent="orange" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_1"> <label class="md-header__button md-icon" title="Switch to light mode" for="__palette_0" hidden> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label class="md-header__button md-icon" for="__search"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg> </label> <div class="md-search" data-md-component="search" role="dialog"> <label class="md-search__overlay" for="__search"></label> <div class="md-search__inner" role="search"> <form class="md-search__form" name="search"> <input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required> <label class="md-search__icon md-icon" for="__search"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg> </label> <nav class="md-search__options" aria-label="Search"> <button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg> </button> </nav> </form> <div class="md-search__output"> <div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix> <div class="md-search-result" data-md-component="search-result"> <div class="md-search-result__meta"> Initializing search </div> <ol class="md-search-result__list" role="presentation"></ol> </div> </div> </div> </div> </div> <div class="md-header__source"> <a href="https://github.com/inveniosoftware/docs-invenio-rdm" title="Go to repository" class="md-source" data-md-component="source"> <div class="md-source__icon md-icon"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81"/></svg> </div> <div class="md-source__repository"> GitHub </div> </a> </div> </nav> </header> <div class="md-container" data-md-component="container"> <nav class="md-tabs" aria-label="Tabs" data-md-component="tabs"> <div class="md-grid"> <ul class="md-tabs__list"> <li class="md-tabs__item"> <a href="../.." class="md-tabs__link"> Home </a> </li> <li class="md-tabs__item"> <a href="../../features/" class="md-tabs__link"> Features </a> </li> <li class="md-tabs__item"> <a href="../../install/" class="md-tabs__link"> Install </a> </li> <li class="md-tabs__item md-tabs__item--active"> <a href="../" class="md-tabs__link"> Customize </a> </li> <li class="md-tabs__item"> <a href="../../develop/" class="md-tabs__link"> Develop </a> </li> <li class="md-tabs__item"> <a href="../../deploy/" class="md-tabs__link"> Deploy </a> </li> <li class="md-tabs__item"> <a href="../../reference/" class="md-tabs__link"> Reference </a> </li> <li class="md-tabs__item"> <a href="../../releases/" class="md-tabs__link"> Releases </a> </li> <li class="md-tabs__item"> <a href="../../maintenance/" class="md-tabs__link"> Maintainers </a> </li> <li class="md-tabs__item"> <a href="../../contribute/" class="md-tabs__link"> Onboard & Contribute </a> </li> </ul> </div> </nav> <main class="md-main" data-md-component="main"> <div class="md-main__inner md-grid"> <div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" > <div class="md-sidebar__scrollwrap"> <div class="md-sidebar__inner"> <nav class="md-nav md-nav--primary md-nav--lifted" aria-label="Navigation" data-md-level="0"> <label class="md-nav__title" for="__drawer"> <a href="../.." title="Turn-key research data management repository" class="md-nav__button md-logo" aria-label="Turn-key research data management repository" data-md-component="logo"> <img src="../../images/logo-rdm.png" alt="logo"> </a> Turn-key research data management repository </label> <div class="md-nav__source"> <a href="https://github.com/inveniosoftware/docs-invenio-rdm" title="Go to repository" class="md-source" data-md-component="source"> <div class="md-source__icon md-icon"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc.--><path d="M439.55 236.05 244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81"/></svg> </div> <div class="md-source__repository"> GitHub </div> </a> </div> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../.." class="md-nav__link"> <span class="md-ellipsis"> Home </span> </a> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2" > <label class="md-nav__link" for="__nav_2" id="__nav_2_label" tabindex="0"> <span class="md-ellipsis"> Features </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="1" aria-labelledby="__nav_2_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_2"> <span class="md-nav__icon md-icon"></span> Features </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../features/" class="md-nav__link"> <span class="md-ellipsis"> Overview </span> </a> </li> <li class="md-nav__item"> <a href="../../features/ux/" class="md-nav__link"> <span class="md-ellipsis"> Beautiful UX </span> </a> </li> <li class="md-nav__item"> <a href="../../features/scalable/" class="md-nav__link"> <span class="md-ellipsis"> Highly scalable </span> </a> </li> <li class="md-nav__item"> <a href="../../features/customization/" class="md-nav__link"> <span class="md-ellipsis"> Customizable </span> </a> </li> <li class="md-nav__item"> <a href="../../features/interoperable/" class="md-nav__link"> <span class="md-ellipsis"> Interoperable </span> </a> </li> <li class="md-nav__item"> <a href="../../features/powerful/" class="md-nav__link"> <span class="md-ellipsis"> Powerful </span> </a> </li> <li class="md-nav__item"> <a href="../../features/secure/" class="md-nav__link"> <span class="md-ellipsis"> Secure </span> </a> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_8" > <label class="md-nav__link" for="__nav_2_8" id="__nav_2_8_label" tabindex="0"> <span class="md-ellipsis"> Features walk-through </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_8_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_2_8"> <span class="md-nav__icon md-icon"></span> Features walk-through </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../features/features-walk-through/" class="md-nav__link"> <span class="md-ellipsis"> Features overview </span> </a> </li> <li class="md-nav__item"> <a href="../../features/features-walk-through/banners/" class="md-nav__link"> <span class="md-ellipsis"> Site banners </span> </a> </li> <li class="md-nav__item"> <a href="../../features/features-walk-through/notifications/" class="md-nav__link"> <span class="md-ellipsis"> Notifications </span> </a> </li> <li class="md-nav__item"> <a href="../../features/features-walk-through/access_requests/" class="md-nav__link"> <span class="md-ellipsis"> Access requests </span> </a> </li> </ul> </nav> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_3" > <label class="md-nav__link" for="__nav_3" id="__nav_3_label" tabindex="0"> <span class="md-ellipsis"> Install </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="1" aria-labelledby="__nav_3_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_3"> <span class="md-nav__icon md-icon"></span> Install </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../install/" class="md-nav__link"> <span class="md-ellipsis"> Quick start </span> </a> </li> <li class="md-nav__item"> <a href="../../install/requirements/" class="md-nav__link"> <span class="md-ellipsis"> System requirements </span> </a> </li> <li class="md-nav__item"> <a href="../../install/cli/" class="md-nav__link"> <span class="md-ellipsis"> Install CLI </span> </a> </li> <li class="md-nav__item"> <a href="../../install/scaffold/" class="md-nav__link"> <span class="md-ellipsis"> Scaffold </span> </a> </li> <li class="md-nav__item"> <a href="../../install/build-setup-run/" class="md-nav__link"> <span class="md-ellipsis"> Build, setup & run </span> </a> </li> <li class="md-nav__item"> <a href="../../install/configuration/" class="md-nav__link"> <span class="md-ellipsis"> Configure </span> </a> </li> <li class="md-nav__item"> <a href="../../install/run/" class="md-nav__link"> <span class="md-ellipsis"> Use </span> </a> </li> <li class="md-nav__item"> <a href="../../install/migrate/" class="md-nav__link"> <span class="md-ellipsis"> Migrate </span> </a> </li> <li class="md-nav__item"> <a href="../../install/destroy/" class="md-nav__link"> <span class="md-ellipsis"> Destroy </span> </a> </li> <li class="md-nav__item"> <a href="../../install/troubleshoot/" class="md-nav__link"> <span class="md-ellipsis"> Troubleshooting </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4" checked> <label class="md-nav__link" for="__nav_4" id="__nav_4_label" tabindex=""> <span class="md-ellipsis"> Customize </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="1" aria-labelledby="__nav_4_label" aria-expanded="true"> <label class="md-nav__title" for="__nav_4"> <span class="md-nav__icon md-icon"></span> Customize </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../" class="md-nav__link"> <span class="md-ellipsis"> Overview </span> </a> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4_2" > <label class="md-nav__link" for="__nav_4_2" id="__nav_4_2_label" tabindex="0"> <span class="md-ellipsis"> Look-and-feel </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_2_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_4_2"> <span class="md-nav__icon md-icon"></span> Look-and-feel </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../look-and-feel/" class="md-nav__link"> <span class="md-ellipsis"> Overview </span> </a> </li> <li class="md-nav__item"> <a href="../look-and-feel/logo/" class="md-nav__link"> <span class="md-ellipsis"> Change logo </span> </a> </li> <li class="md-nav__item"> <a href="../look-and-feel/templates/" class="md-nav__link"> <span class="md-ellipsis"> Change templates </span> </a> </li> <li class="md-nav__item"> <a href="../look-and-feel/theme/" class="md-nav__link"> <span class="md-ellipsis"> Change theme </span> </a> </li> <li class="md-nav__item"> <a href="../look-and-feel/font/" class="md-nav__link"> <span class="md-ellipsis"> Change font </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--active"> <input class="md-nav__toggle md-toggle" type="checkbox" id="__toc"> <label class="md-nav__link md-nav__link--active" for="__toc"> <span class="md-ellipsis"> Authentication </span> <span class="md-nav__icon md-icon"></span> </label> <a href="./" class="md-nav__link md-nav__link--active"> <span class="md-ellipsis"> Authentication </span> </a> <nav class="md-nav md-nav--secondary" aria-label="Table of contents"> <label class="md-nav__title" for="__toc"> <span class="md-nav__icon md-icon"></span> Table of contents </label> <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix> <li class="md-nav__item"> <a href="#local-authentication" class="md-nav__link"> <span class="md-ellipsis"> Local Authentication </span> </a> <nav class="md-nav" aria-label="Local Authentication"> <ul class="md-nav__list"> <li class="md-nav__item"> <a href="#disabling-local-authentication" class="md-nav__link"> <span class="md-ellipsis"> Disabling local authentication </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item"> <a href="#external-authentication" class="md-nav__link"> <span class="md-ellipsis"> External authentication </span> </a> <nav class="md-nav" aria-label="External authentication"> <ul class="md-nav__list"> <li class="md-nav__item"> <a href="#oauth" class="md-nav__link"> <span class="md-ellipsis"> OAuth </span> </a> <nav class="md-nav" aria-label="OAuth"> <ul class="md-nav__list"> <li class="md-nav__item"> <a href="#orcid" class="md-nav__link"> <span class="md-ellipsis"> ORCID </span> </a> </li> <li class="md-nav__item"> <a href="#github" class="md-nav__link"> <span class="md-ellipsis"> GITHUB </span> </a> </li> <li class="md-nav__item"> <a href="#keycloak" class="md-nav__link"> <span class="md-ellipsis"> Keycloak </span> </a> <nav class="md-nav" aria-label="Keycloak"> <ul class="md-nav__list"> <li class="md-nav__item"> <a href="#configuration" class="md-nav__link"> <span class="md-ellipsis"> Configuration </span> </a> </li> <li class="md-nav__item"> <a href="#tweaking-configuration" class="md-nav__link"> <span class="md-ellipsis"> Tweaking configuration </span> </a> </li> <li class="md-nav__item"> <a href="#multiple-keycloak-authentication-providers" class="md-nav__link"> <span class="md-ellipsis"> Multiple Keycloak authentication providers </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item"> <a href="#openaire-aai" class="md-nav__link"> <span class="md-ellipsis"> OpenAIRE AAI </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item"> <a href="#login-automatic-redirection" class="md-nav__link"> <span class="md-ellipsis"> Login automatic redirection </span> </a> </li> <li class="md-nav__item"> <a href="#user-profile-update" class="md-nav__link"> <span class="md-ellipsis"> User profile update </span> </a> </li> <li class="md-nav__item"> <a href="#auto-confirm-user" class="md-nav__link"> <span class="md-ellipsis"> Auto-confirm user </span> </a> </li> <li class="md-nav__item"> <a href="#on-the-precedence-mask" class="md-nav__link"> <span class="md-ellipsis"> On the precedence mask </span> </a> </li> <li class="md-nav__item"> <a href="#defining-post-logout-url" class="md-nav__link"> <span class="md-ellipsis"> Defining post logout url </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item"> <a href="#security" class="md-nav__link"> <span class="md-ellipsis"> Security </span> </a> </li> <li class="md-nav__item"> <a href="#advanced-integrations" class="md-nav__link"> <span class="md-ellipsis"> Advanced integrations </span> </a> <nav class="md-nav" aria-label="Advanced integrations"> <ul class="md-nav__list"> <li class="md-nav__item"> <a href="#custom-login-page" class="md-nav__link"> <span class="md-ellipsis"> Custom login page </span> </a> </li> <li class="md-nav__item"> <a href="#custom-user-registration-form" class="md-nav__link"> <span class="md-ellipsis"> Custom user registration form </span> </a> </li> <li class="md-nav__item"> <a href="#custom-user-info" class="md-nav__link"> <span class="md-ellipsis"> Custom user info </span> </a> </li> <li class="md-nav__item"> <a href="#new-oauth-plugins" class="md-nav__link"> <span class="md-ellipsis"> New OAuth plugins </span> </a> </li> <li class="md-nav__item"> <a href="#allowdeny-user-login" class="md-nav__link"> <span class="md-ellipsis"> Allow/deny user login </span> </a> </li> <li class="md-nav__item"> <a href="#saml-integration" class="md-nav__link"> <span class="md-ellipsis"> SAML integration </span> </a> <nav class="md-nav" aria-label="SAML integration"> <ul class="md-nav__list"> <li class="md-nav__item"> <a href="#prerequisites" class="md-nav__link"> <span class="md-ellipsis"> Prerequisites </span> </a> </li> <li class="md-nav__item"> <a href="#server-information" class="md-nav__link"> <span class="md-ellipsis"> Server information </span> </a> </li> <li class="md-nav__item"> <a href="#configuration_1" class="md-nav__link"> <span class="md-ellipsis"> Configuration </span> </a> </li> <li class="md-nav__item"> <a href="#automatically-confirm-users" class="md-nav__link"> <span class="md-ellipsis"> Automatically confirm users </span> </a> <nav class="md-nav" aria-label="Automatically confirm users"> <ul class="md-nav__list"> <li class="md-nav__item"> <a href="#example-configurations-element" class="md-nav__link"> <span class="md-ellipsis"> Example configurations element </span> </a> </li> <li class="md-nav__item"> <a href="#troubleshooting-saml-configuration" class="md-nav__link"> <span class="md-ellipsis"> Troubleshooting SAML configuration </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item"> <a href="#show-the-login-button" class="md-nav__link"> <span class="md-ellipsis"> Show the login button </span> </a> </li> <li class="md-nav__item"> <a href="#multiple-saml-authentication-providers" class="md-nav__link"> <span class="md-ellipsis"> Multiple SAML authentication providers </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item"> <a href="#groups" class="md-nav__link"> <span class="md-ellipsis"> Groups </span> </a> <nav class="md-nav" aria-label="Groups"> <ul class="md-nav__list"> <li class="md-nav__item"> <a href="#add-groups" class="md-nav__link"> <span class="md-ellipsis"> Add groups </span> </a> </li> <li class="md-nav__item"> <a href="#assign-groups-on-login" class="md-nav__link"> <span class="md-ellipsis"> Assign groups on login </span> </a> </li> </ul> </nav> </li> </ul> </nav> </li> </ul> </nav> </li> <li class="md-nav__item"> <a href="../emails/" class="md-nav__link"> <span class="md-ellipsis"> Sending emails </span> </a> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4_5" > <label class="md-nav__link" for="__nav_4_5" id="__nav_4_5_label" tabindex="0"> <span class="md-ellipsis"> Search </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_5_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_4_5"> <span class="md-nav__icon md-icon"></span> Search </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../search/" class="md-nav__link"> <span class="md-ellipsis"> Change facets/sorting </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4_6" > <label class="md-nav__link" for="__nav_4_6" id="__nav_4_6_label" tabindex="0"> <span class="md-ellipsis"> Vocabularies </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_6_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_4_6"> <span class="md-nav__icon md-icon"></span> Vocabularies </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../vocabularies/" class="md-nav__link"> <span class="md-ellipsis"> Overview </span> </a> </li> <li class="md-nav__item"> <a href="../vocabularies/resource_types/" class="md-nav__link"> <span class="md-ellipsis"> Resource types </span> </a> </li> <li class="md-nav__item"> <a href="../vocabularies/affiliations/" class="md-nav__link"> <span class="md-ellipsis"> Affiliations </span> </a> </li> <li class="md-nav__item"> <a href="../vocabularies/names/" class="md-nav__link"> <span class="md-ellipsis"> Names </span> </a> </li> <li class="md-nav__item"> <a href="../vocabularies/funding/" class="md-nav__link"> <span class="md-ellipsis"> Funding </span> </a> </li> <li class="md-nav__item"> <a href="../vocabularies/subjects/" class="md-nav__link"> <span class="md-ellipsis"> Subjects </span> </a> </li> <li class="md-nav__item"> <a href="../vocabularies/users/" class="md-nav__link"> <span class="md-ellipsis"> Users </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4_7" > <label class="md-nav__link" for="__nav_4_7" id="__nav_4_7_label" tabindex="0"> <span class="md-ellipsis"> Metadata </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_7_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_4_7"> <span class="md-nav__icon md-icon"></span> Metadata </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../metadata/optional_fields/" class="md-nav__link"> <span class="md-ellipsis"> Optional metadata </span> </a> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4_7_2" > <label class="md-nav__link" for="__nav_4_7_2" id="__nav_4_7_2_label" tabindex="0"> <span class="md-ellipsis"> Custom fields </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="3" aria-labelledby="__nav_4_7_2_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_4_7_2"> <span class="md-nav__icon md-icon"></span> Custom fields </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../metadata/custom_fields/records/" class="md-nav__link"> <span class="md-ellipsis"> Add fields to records </span> </a> </li> <li class="md-nav__item"> <a href="../metadata/custom_fields/communities/" class="md-nav__link"> <span class="md-ellipsis"> Add fields to communities </span> </a> </li> </ul> </nav> </li> </ul> </nav> </li> <li class="md-nav__item"> <a href="../record_landing_page/" class="md-nav__link"> <span class="md-ellipsis"> Record landing page </span> </a> </li> <li class="md-nav__item"> <a href="../static_pages/" class="md-nav__link"> <span class="md-ellipsis"> Static pages </span> </a> </li> <li class="md-nav__item"> <a href="../dois/" class="md-nav__link"> <span class="md-ellipsis"> DOI registration </span> </a> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4_11" > <label class="md-nav__link" for="__nav_4_11" id="__nav_4_11_label" tabindex="0"> <span class="md-ellipsis"> Other PIDs integrations </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_11_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_4_11"> <span class="md-nav__icon md-icon"></span> Other PIDs integrations </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../other-pids/urns/" class="md-nav__link"> <span class="md-ellipsis"> DNB URNs </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item"> <a href="../s3/" class="md-nav__link"> <span class="md-ellipsis"> Storage </span> </a> </li> <li class="md-nav__item"> <a href="../upload_limits/" class="md-nav__link"> <span class="md-ellipsis"> Upload Limits </span> </a> </li> <li class="md-nav__item"> <a href="../metadata_only/" class="md-nav__link"> <span class="md-ellipsis"> Metadata-only records </span> </a> </li> <li class="md-nav__item"> <a href="../Logging/" class="md-nav__link"> <span class="md-ellipsis"> Logging </span> </a> </li> <li class="md-nav__item"> <a href="../i18n-and-l10n/" class="md-nav__link"> <span class="md-ellipsis"> Internationalisation (i18n) and Localisation (l10n) </span> </a> </li> <li class="md-nav__item"> <a href="../notifications/" class="md-nav__link"> <span class="md-ellipsis"> Notifications </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5" > <label class="md-nav__link" for="__nav_5" id="__nav_5_label" tabindex="0"> <span class="md-ellipsis"> Develop </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="1" aria-labelledby="__nav_5_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_5"> <span class="md-nav__icon md-icon"></span> Develop </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../develop/" class="md-nav__link"> <span class="md-ellipsis"> Overview </span> </a> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5_2" > <label class="md-nav__link" for="__nav_5_2" id="__nav_5_2_label" tabindex="0"> <span class="md-ellipsis"> Getting started </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_5_2_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_5_2"> <span class="md-nav__icon md-icon"></span> Getting started </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../develop/getting-started/source-code/" class="md-nav__link"> <span class="md-ellipsis"> Source code </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/getting-started/package-development/" class="md-nav__link"> <span class="md-ellipsis"> Package development </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/getting-started/instance-development/" class="md-nav__link"> <span class="md-ellipsis"> Instance development </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/getting-started/debugging/" class="md-nav__link"> <span class="md-ellipsis"> Debugging </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/getting-started/code-style/" class="md-nav__link"> <span class="md-ellipsis"> Coding style </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/getting-started/virtualenvs/" class="md-nav__link"> <span class="md-ellipsis"> Virtual environments </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/getting-started/help/" class="md-nav__link"> <span class="md-ellipsis"> Getting help </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/process/" class="md-nav__link"> <span class="md-ellipsis"> Development process </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5_3" > <label class="md-nav__link" for="__nav_5_3" id="__nav_5_3_label" tabindex="0"> <span class="md-ellipsis"> Best practices </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_5_3_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_5_3"> <span class="md-nav__icon md-icon"></span> Best practices </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../develop/best-practices/accessibility/" class="md-nav__link"> <span class="md-ellipsis"> Accessibility (a11y) </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/best-practices/commits/" class="md-nav__link"> <span class="md-ellipsis"> Commits, PRs & reviews </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/best-practices/css-js/" class="md-nav__link"> <span class="md-ellipsis"> CSS/JavaScript </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/best-practices/react/" class="md-nav__link"> <span class="md-ellipsis"> React </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/best-practices/ui/" class="md-nav__link"> <span class="md-ellipsis"> User interface </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5_4" > <label class="md-nav__link" for="__nav_5_4" id="__nav_5_4_label" tabindex="0"> <span class="md-ellipsis"> Topic guides </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_5_4_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_5_4"> <span class="md-nav__icon md-icon"></span> Topic guides </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../develop/topics/resource/" class="md-nav__link"> <span class="md-ellipsis"> Building resources </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/topics/service/" class="md-nav__link"> <span class="md-ellipsis"> Building services </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/topics/serializers/" class="md-nav__link"> <span class="md-ellipsis"> Building serializers </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/topics/uow/" class="md-nav__link"> <span class="md-ellipsis"> Grouping atomic operations </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/topics/validation/" class="md-nav__link"> <span class="md-ellipsis"> Sanitize input data </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/topics/theming/" class="md-nav__link"> <span class="md-ellipsis"> Theming </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/topics/administration_panel/" class="md-nav__link"> <span class="md-ellipsis"> Administration panel </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5_5" > <label class="md-nav__link" for="__nav_5_5" id="__nav_5_5_label" tabindex="0"> <span class="md-ellipsis"> How-to guides </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_5_5_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_5_5"> <span class="md-nav__icon md-icon"></span> How-to guides </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../develop/howtos/i18n/" class="md-nav__link"> <span class="md-ellipsis"> Translation (i18n) </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/howtos/custom_fields/" class="md-nav__link"> <span class="md-ellipsis"> Create a new custom field </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/howtos/custom_code/" class="md-nav__link"> <span class="md-ellipsis"> Create custom code and views </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/howtos/add_javascript/" class="md-nav__link"> <span class="md-ellipsis"> Add JavaScript </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/howtos/override_components/" class="md-nav__link"> <span class="md-ellipsis"> Override UI React components </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/howtos/search_terms_migration/" class="md-nav__link"> <span class="md-ellipsis"> Create search terms mappings </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/howtos/alembic/" class="md-nav__link"> <span class="md-ellipsis"> Create a database migration </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/howtos/security-fix/" class="md-nav__link"> <span class="md-ellipsis"> Fix a vulnerability </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/howtos/dev_email/" class="md-nav__link"> <span class="md-ellipsis"> Test emails locally </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/howtos/route_migration/" class="md-nav__link"> <span class="md-ellipsis"> Migrate legacy routes </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/howtos/backup_search_indices/" class="md-nav__link"> <span class="md-ellipsis"> Back up search indices </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/howtos/restrict_access/" class="md-nav__link"> <span class="md-ellipsis"> Restrict access to pages </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/howtos/notifications/" class="md-nav__link"> <span class="md-ellipsis"> Create and configure notifications </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5_6" > <label class="md-nav__link" for="__nav_5_6" id="__nav_5_6_label" tabindex="0"> <span class="md-ellipsis"> Architecture </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_5_6_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_5_6"> <span class="md-nav__icon md-icon"></span> Architecture </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../develop/architecture/" class="md-nav__link"> <span class="md-ellipsis"> Introduction </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/architecture/infrastructure/" class="md-nav__link"> <span class="md-ellipsis"> Infrastructure </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/architecture/software/" class="md-nav__link"> <span class="md-ellipsis"> Software </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/architecture/runtime/" class="md-nav__link"> <span class="md-ellipsis"> Runtime </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/architecture/requests/" class="md-nav__link"> <span class="md-ellipsis"> Requests </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/architecture/communities/" class="md-nav__link"> <span class="md-ellipsis"> Communities </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/architecture/records/" class="md-nav__link"> <span class="md-ellipsis"> Records </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/architecture/event_handling/" class="md-nav__link"> <span class="md-ellipsis"> Event handling </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/architecture/notifications/" class="md-nav__link"> <span class="md-ellipsis"> Notifications </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/architecture/reading/" class="md-nav__link"> <span class="md-ellipsis"> Recommended reading </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5_7" > <label class="md-nav__link" for="__nav_5_7" id="__nav_5_7_label" tabindex="0"> <span class="md-ellipsis"> Concepts </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_5_7_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_5_7"> <span class="md-nav__icon md-icon"></span> Concepts </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../develop/concepts/concurrency-control/" class="md-nav__link"> <span class="md-ellipsis"> Optimistic concurrency control </span> </a> </li> <li class="md-nav__item"> <a href="../../develop/concepts/transactions/" class="md-nav__link"> <span class="md-ellipsis"> Database transaction management </span> </a> </li> </ul> </nav> </li> </ul> </nav> </li> <li class="md-nav__item"> <a href="../../deploy/" class="md-nav__link"> <span class="md-ellipsis"> Deploy </span> </a> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_7" > <label class="md-nav__link" for="__nav_7" id="__nav_7_label" tabindex="0"> <span class="md-ellipsis"> Reference </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="1" aria-labelledby="__nav_7_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_7"> <span class="md-nav__icon md-icon"></span> Reference </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../reference/" class="md-nav__link"> <span class="md-ellipsis"> Overview </span> </a> </li> <li class="md-nav__item"> <a href="../../reference/cli/" class="md-nav__link"> <span class="md-ellipsis"> CLI </span> </a> </li> <li class="md-nav__item"> <a href="../../reference/configuration/" class="md-nav__link"> <span class="md-ellipsis"> Configuration </span> </a> </li> <li class="md-nav__item"> <a href="../../reference/metadata/" class="md-nav__link"> <span class="md-ellipsis"> Metadata </span> </a> </li> <li class="md-nav__item"> <a href="../../reference/metadata/optional_metadata/" class="md-nav__link"> <span class="md-ellipsis"> Optional metadata </span> </a> </li> <li class="md-nav__item"> <a href="../../reference/oai_pmh/" class="md-nav__link"> <span class="md-ellipsis"> OAI-PMH </span> </a> </li> <li class="md-nav__item"> <a href="../../reference/export_formats/" class="md-nav__link"> <span class="md-ellipsis"> Export formats </span> </a> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_7_8" > <label class="md-nav__link" for="__nav_7_8" id="__nav_7_8_label" tabindex="0"> <span class="md-ellipsis"> REST API </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_7_8_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_7_8"> <span class="md-nav__icon md-icon"></span> REST API </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../reference/rest_api_index/" class="md-nav__link"> <span class="md-ellipsis"> Overview </span> </a> </li> <li class="md-nav__item"> <a href="../../reference/rest_api_quickstart/" class="md-nav__link"> <span class="md-ellipsis"> Quickstart </span> </a> </li> <li class="md-nav__item"> <a href="../../reference/rest_api_communities/" class="md-nav__link"> <span class="md-ellipsis"> Communities </span> </a> </li> <li class="md-nav__item"> <a href="../../reference/rest_api_drafts_records/" class="md-nav__link"> <span class="md-ellipsis"> Drafts and Records </span> </a> </li> <li class="md-nav__item"> <a href="../../reference/rest_api_groups/" class="md-nav__link"> <span class="md-ellipsis"> Groups </span> </a> </li> <li class="md-nav__item"> <a href="../../reference/rest_api_members/" class="md-nav__link"> <span class="md-ellipsis"> Members </span> </a> </li> <li class="md-nav__item"> <a href="../../reference/rest_api_requests/" class="md-nav__link"> <span class="md-ellipsis"> Requests </span> </a> </li> <li class="md-nav__item"> <a href="../../reference/rest_api_users/" class="md-nav__link"> <span class="md-ellipsis"> Users </span> </a> </li> <li class="md-nav__item"> <a href="../../reference/rest_api_reviews/" class="md-nav__link"> <span class="md-ellipsis"> Reviews </span> </a> </li> <li class="md-nav__item"> <a href="../../reference/rest_api_vocabularies/" class="md-nav__link"> <span class="md-ellipsis"> Vocabularies </span> </a> </li> <li class="md-nav__item"> <a href="../../reference/rest_api_names/" class="md-nav__link"> <span class="md-ellipsis"> Names </span> </a> </li> <li class="md-nav__item"> <a href="../../reference/rest_api_funders/" class="md-nav__link"> <span class="md-ellipsis"> Funders </span> </a> </li> <li class="md-nav__item"> <a href="../../reference/rest_api_awards/" class="md-nav__link"> <span class="md-ellipsis"> Awards </span> </a> </li> <li class="md-nav__item"> <a href="../../reference/rest_api_oaipmh_sets/" class="md-nav__link"> <span class="md-ellipsis"> OAI-PMH Sets </span> </a> </li> <li class="md-nav__item"> <a href="../../reference/rest_api_statistics/" class="md-nav__link"> <span class="md-ellipsis"> Statistics </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_7_9" > <label class="md-nav__link" for="__nav_7_9" id="__nav_7_9_label" tabindex="0"> <span class="md-ellipsis"> Custom fields </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_7_9_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_7_9"> <span class="md-nav__icon md-icon"></span> Custom fields </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../reference/custom_fields/widgets/" class="md-nav__link"> <span class="md-ellipsis"> UI widgets </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_7_10" > <label class="md-nav__link" for="__nav_7_10" id="__nav_7_10_label" tabindex="0"> <span class="md-ellipsis"> Administration </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_7_10_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_7_10"> <span class="md-nav__icon md-icon"></span> Administration </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../reference/administration_reference/" class="md-nav__link"> <span class="md-ellipsis"> Reference </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item"> <a href="../../reference/file_storage/" class="md-nav__link"> <span class="md-ellipsis"> File storage </span> </a> </li> <li class="md-nav__item"> <a href="../../reference/statistics/" class="md-nav__link"> <span class="md-ellipsis"> Usage statistics </span> </a> </li> <li class="md-nav__item"> <a href="../../reference/notifications/" class="md-nav__link"> <span class="md-ellipsis"> Notifications </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_8" > <label class="md-nav__link" for="__nav_8" id="__nav_8_label" tabindex="0"> <span class="md-ellipsis"> Releases </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="1" aria-labelledby="__nav_8_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_8"> <span class="md-nav__icon md-icon"></span> Releases </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../releases/" class="md-nav__link"> <span class="md-ellipsis"> Overview </span> </a> </li> <li class="md-nav__item"> <a href="../../releases/maintenance-policy/" class="md-nav__link"> <span class="md-ellipsis"> Maintenance policy </span> </a> </li> <li class="md-nav__item"> <a href="../../releases/upgrade-policy/" class="md-nav__link"> <span class="md-ellipsis"> Upgrade policy </span> </a> </li> <li class="md-nav__item"> <a href="../../releases/security-policy/" class="md-nav__link"> <span class="md-ellipsis"> Security policy </span> </a> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_8_5" > <label class="md-nav__link" for="__nav_8_5" id="__nav_8_5_label" tabindex="0"> <span class="md-ellipsis"> Version v12.0 LTS </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_8_5_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_8_5"> <span class="md-nav__icon md-icon"></span> Version v12.0 LTS </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../releases/v12/version-v12.0.0/" class="md-nav__link"> <span class="md-ellipsis"> Release Notes v12.0 </span> </a> </li> <li class="md-nav__item"> <a href="../../releases/v12/upgrade-v12.0/" class="md-nav__link"> <span class="md-ellipsis"> Upgrade from v11 to v12 </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_8_6" > <label class="md-nav__link" for="__nav_8_6" id="__nav_8_6_label" tabindex="0"> <span class="md-ellipsis"> Version v11.0 STS </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_8_6_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_8_6"> <span class="md-nav__icon md-icon"></span> Version v11.0 STS </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../releases/v11/version-v11.0.0/" class="md-nav__link"> <span class="md-ellipsis"> Release Notes v11.0 </span> </a> </li> <li class="md-nav__item"> <a href="../../releases/v11/upgrade-v11.0/" class="md-nav__link"> <span class="md-ellipsis"> Upgrade from v10 to v11 </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_8_7" > <label class="md-nav__link" for="__nav_8_7" id="__nav_8_7_label" tabindex="0"> <span class="md-ellipsis"> Version v9.1 LTS </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_8_7_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_8_7"> <span class="md-nav__icon md-icon"></span> Version v9.1 LTS </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../releases/v9/version-v9.1.0/" class="md-nav__link"> <span class="md-ellipsis"> Release Notes v9.1 </span> </a> </li> <li class="md-nav__item"> <a href="../../releases/v9/version-v9.0.0/" class="md-nav__link"> <span class="md-ellipsis"> Release Notes v9.0 </span> </a> </li> <li class="md-nav__item"> <a href="../../releases/migrate-docker-images/" class="md-nav__link"> <span class="md-ellipsis"> Migrate Docker images </span> </a> </li> <li class="md-nav__item"> <a href="../../releases/v9/migrate-opensearch-v9/" class="md-nav__link"> <span class="md-ellipsis"> Migrate v9 to OpenSearch </span> </a> </li> <li class="md-nav__item"> <a href="../../releases/v9/upgrade-v9.0/" class="md-nav__link"> <span class="md-ellipsis"> Upgrade from v8 to v9 </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_8_8" > <label class="md-nav__link" for="__nav_8_8" id="__nav_8_8_label" tabindex="0"> <span class="md-ellipsis"> Legacy Versions </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="2" aria-labelledby="__nav_8_8_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_8_8"> <span class="md-nav__icon md-icon"></span> Legacy Versions </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_8_8_1" > <label class="md-nav__link" for="__nav_8_8_1" id="__nav_8_8_1_label" tabindex="0"> <span class="md-ellipsis"> Version v10.1 </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="3" aria-labelledby="__nav_8_8_1_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_8_8_1"> <span class="md-nav__icon md-icon"></span> Version v10.1 </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../releases/v10/version-v10.1.0/" class="md-nav__link"> <span class="md-ellipsis"> Release Notes v10.1 </span> </a> </li> <li class="md-nav__item"> <a href="../../releases/v10/version-v10.0.0/" class="md-nav__link"> <span class="md-ellipsis"> Release Notes v10.0 </span> </a> </li> <li class="md-nav__item"> <a href="../../releases/v10/upgrade-v10.0/" class="md-nav__link"> <span class="md-ellipsis"> Upgrade from v9 to v10 </span> </a> </li> <li class="md-nav__item"> <a href="../../releases/migrate-docker-images/" class="md-nav__link"> <span class="md-ellipsis"> Migrate Docker images </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_8_8_2" > <label class="md-nav__link" for="__nav_8_8_2" id="__nav_8_8_2_label" tabindex="0"> <span class="md-ellipsis"> Version v8.0 </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="3" aria-labelledby="__nav_8_8_2_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_8_8_2"> <span class="md-nav__icon md-icon"></span> Version v8.0 </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../releases/v8/version-v8.0.0/" class="md-nav__link"> <span class="md-ellipsis"> Release Notes v8.0 </span> </a> </li> <li class="md-nav__item"> <a href="../../releases/v8/upgrade-v8.0/" class="md-nav__link"> <span class="md-ellipsis"> Upgrade from v7 to v8 </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_8_8_3" > <label class="md-nav__link" for="__nav_8_8_3" id="__nav_8_8_3_label" tabindex="0"> <span class="md-ellipsis"> Version v7.0 </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="3" aria-labelledby="__nav_8_8_3_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_8_8_3"> <span class="md-nav__icon md-icon"></span> Version v7.0 </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../releases/v7/version-v7.0.0/" class="md-nav__link"> <span class="md-ellipsis"> Release Notes v7.0 </span> </a> </li> <li class="md-nav__item"> <a href="../../releases/v7/upgrade-v7.0/" class="md-nav__link"> <span class="md-ellipsis"> Upgrade from v6 to v7 </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_8_8_4" > <label class="md-nav__link" for="__nav_8_8_4" id="__nav_8_8_4_label" tabindex="0"> <span class="md-ellipsis"> Version v6.0.5 </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="3" aria-labelledby="__nav_8_8_4_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_8_8_4"> <span class="md-nav__icon md-icon"></span> Version v6.0.5 </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../releases/v6/version-v6.0.5/" class="md-nav__link"> <span class="md-ellipsis"> Release Notes v6.0.5 </span> </a> </li> <li class="md-nav__item"> <a href="../../releases/v6/upgrade-v6.0.5/" class="md-nav__link"> <span class="md-ellipsis"> Upgrade from v6.0.x to v6.0.5 </span> </a> </li> <li class="md-nav__item"> <a href="../../releases/v6/version-v6.0.0/" class="md-nav__link"> <span class="md-ellipsis"> Release Notes v6.0.0 </span> </a> </li> <li class="md-nav__item"> <a href="../../releases/v6/upgrade-v6.0/" class="md-nav__link"> <span class="md-ellipsis"> Upgrade from v4 to v6 </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_8_8_5" > <label class="md-nav__link" for="__nav_8_8_5" id="__nav_8_8_5_label" tabindex="0"> <span class="md-ellipsis"> Version v5.0 </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="3" aria-labelledby="__nav_8_8_5_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_8_8_5"> <span class="md-nav__icon md-icon"></span> Version v5.0 </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../releases/v5/version-v5.0.0/" class="md-nav__link"> <span class="md-ellipsis"> Release Notes v5.0 </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_8_8_6" > <label class="md-nav__link" for="__nav_8_8_6" id="__nav_8_8_6_label" tabindex="0"> <span class="md-ellipsis"> Version v4.0 </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="3" aria-labelledby="__nav_8_8_6_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_8_8_6"> <span class="md-nav__icon md-icon"></span> Version v4.0 </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../releases/v4/version-v4.0.0/" class="md-nav__link"> <span class="md-ellipsis"> Release Notes v4.0 </span> </a> </li> <li class="md-nav__item"> <a href="../../releases/v4/upgrade-v4.0/" class="md-nav__link"> <span class="md-ellipsis"> Upgrade from v3 to v4 </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_8_8_7" > <label class="md-nav__link" for="__nav_8_8_7" id="__nav_8_8_7_label" tabindex="0"> <span class="md-ellipsis"> Version v3.0 </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="3" aria-labelledby="__nav_8_8_7_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_8_8_7"> <span class="md-nav__icon md-icon"></span> Version v3.0 </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../releases/v3/version-v3.0.0/" class="md-nav__link"> <span class="md-ellipsis"> Release Notes v3.0 </span> </a> </li> <li class="md-nav__item"> <a href="../../releases/v3/upgrade-v3.0/" class="md-nav__link"> <span class="md-ellipsis"> Upgrade from v2 to v3 </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_8_8_8" > <label class="md-nav__link" for="__nav_8_8_8" id="__nav_8_8_8_label" tabindex="0"> <span class="md-ellipsis"> Version v2.0 </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="3" aria-labelledby="__nav_8_8_8_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_8_8_8"> <span class="md-nav__icon md-icon"></span> Version v2.0 </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../releases/v2/version-v2.0.0/" class="md-nav__link"> <span class="md-ellipsis"> Release Notes v2.0 </span> </a> </li> <li class="md-nav__item"> <a href="../../releases/v2/upgrade-v2.0/" class="md-nav__link"> <span class="md-ellipsis"> Upgrade from v1 to v2 </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_8_8_9" > <label class="md-nav__link" for="__nav_8_8_9" id="__nav_8_8_9_label" tabindex="0"> <span class="md-ellipsis"> Version v1.0 </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="3" aria-labelledby="__nav_8_8_9_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_8_8_9"> <span class="md-nav__icon md-icon"></span> Version v1.0 </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../releases/v1/version-v1.0.0/" class="md-nav__link"> <span class="md-ellipsis"> Release Notes v1.0 </span> </a> </li> </ul> </nav> </li> </ul> </nav> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_9" > <label class="md-nav__link" for="__nav_9" id="__nav_9_label" tabindex="0"> <span class="md-ellipsis"> Maintainers </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="1" aria-labelledby="__nav_9_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_9"> <span class="md-nav__icon md-icon"></span> Maintainers </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../maintenance/" class="md-nav__link"> <span class="md-ellipsis"> Overview </span> </a> </li> <li class="md-nav__item"> <a href="../../maintenance/newcomers/" class="md-nav__link"> <span class="md-ellipsis"> Newcomers </span> </a> </li> <li class="md-nav__item"> <a href="../../maintenance/board-workflow/" class="md-nav__link"> <span class="md-ellipsis"> Sprintboard workflow </span> </a> </li> <li class="md-nav__item"> <a href="../../maintenance/pr-community-board/" class="md-nav__link"> <span class="md-ellipsis"> Community PR board </span> </a> </li> <li class="md-nav__item"> <a href="../../maintenance/release-management/" class="md-nav__link"> <span class="md-ellipsis"> Release management </span> </a> </li> <li class="md-nav__item"> <a href="../../maintenance/modules/" class="md-nav__link"> <span class="md-ellipsis"> Modules on GitHub </span> </a> </li> <li class="md-nav__item"> <a href="../../maintenance/branch-management/" class="md-nav__link"> <span class="md-ellipsis"> Branch management </span> </a> </li> <li class="md-nav__item"> <a href="../../maintenance/demosite/" class="md-nav__link"> <span class="md-ellipsis"> Demo site & docs </span> </a> </li> <li class="md-nav__item"> <a href="../../maintenance/docker-images/" class="md-nav__link"> <span class="md-ellipsis"> Docker images </span> </a> </li> <li class="md-nav__item"> <a href="../../maintenance/rfcs/" class="md-nav__link"> <span class="md-ellipsis"> RFCs </span> </a> </li> <li class="md-nav__item"> <a href="../../maintenance/documentation/" class="md-nav__link"> <span class="md-ellipsis"> Writing documentation </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item md-nav__item--nested"> <input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_10" > <label class="md-nav__link" for="__nav_10" id="__nav_10_label" tabindex="0"> <span class="md-ellipsis"> Onboard & Contribute </span> <span class="md-nav__icon md-icon"></span> </label> <nav class="md-nav" data-md-level="1" aria-labelledby="__nav_10_label" aria-expanded="false"> <label class="md-nav__title" for="__nav_10"> <span class="md-nav__icon md-icon"></span> Onboard & Contribute </label> <ul class="md-nav__list" data-md-scrollfix> <li class="md-nav__item"> <a href="../../contribute/" class="md-nav__link"> <span class="md-ellipsis"> Overview </span> </a> </li> <li class="md-nav__item"> <a href="../../contribute/onboarding/" class="md-nav__link"> <span class="md-ellipsis"> Onboarding </span> </a> </li> <li class="md-nav__item"> <a href="../../contribute/code-of-conduct/" class="md-nav__link"> <span class="md-ellipsis"> Code of conduct </span> </a> </li> <li class="md-nav__item"> <a href="../../contribute/copyright-policy/" class="md-nav__link"> <span class="md-ellipsis"> Copyright policy </span> </a> </li> <li class="md-nav__item"> <a href="../../contribute/roadmap/" class="md-nav__link"> <span class="md-ellipsis"> Roadmap </span> </a> </li> <li class="md-nav__item"> <a href="../../contribute/translators-guide/" class="md-nav__link"> <span class="md-ellipsis"> Translators guide </span> </a> </li> </ul> </nav> </li> </ul> </nav> </div> </div> </div> <div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" > <div class="md-sidebar__scrollwrap"> <div class="md-sidebar__inner"> <nav class="md-nav md-nav--secondary" aria-label="Table of contents"> <label class="md-nav__title" for="__toc"> <span class="md-nav__icon md-icon"></span> Table of contents </label> <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix> <li class="md-nav__item"> <a href="#local-authentication" class="md-nav__link"> <span class="md-ellipsis"> Local Authentication </span> </a> <nav class="md-nav" aria-label="Local Authentication"> <ul class="md-nav__list"> <li class="md-nav__item"> <a href="#disabling-local-authentication" class="md-nav__link"> <span class="md-ellipsis"> Disabling local authentication </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item"> <a href="#external-authentication" class="md-nav__link"> <span class="md-ellipsis"> External authentication </span> </a> <nav class="md-nav" aria-label="External authentication"> <ul class="md-nav__list"> <li class="md-nav__item"> <a href="#oauth" class="md-nav__link"> <span class="md-ellipsis"> OAuth </span> </a> <nav class="md-nav" aria-label="OAuth"> <ul class="md-nav__list"> <li class="md-nav__item"> <a href="#orcid" class="md-nav__link"> <span class="md-ellipsis"> ORCID </span> </a> </li> <li class="md-nav__item"> <a href="#github" class="md-nav__link"> <span class="md-ellipsis"> GITHUB </span> </a> </li> <li class="md-nav__item"> <a href="#keycloak" class="md-nav__link"> <span class="md-ellipsis"> Keycloak </span> </a> <nav class="md-nav" aria-label="Keycloak"> <ul class="md-nav__list"> <li class="md-nav__item"> <a href="#configuration" class="md-nav__link"> <span class="md-ellipsis"> Configuration </span> </a> </li> <li class="md-nav__item"> <a href="#tweaking-configuration" class="md-nav__link"> <span class="md-ellipsis"> Tweaking configuration </span> </a> </li> <li class="md-nav__item"> <a href="#multiple-keycloak-authentication-providers" class="md-nav__link"> <span class="md-ellipsis"> Multiple Keycloak authentication providers </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item"> <a href="#openaire-aai" class="md-nav__link"> <span class="md-ellipsis"> OpenAIRE AAI </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item"> <a href="#login-automatic-redirection" class="md-nav__link"> <span class="md-ellipsis"> Login automatic redirection </span> </a> </li> <li class="md-nav__item"> <a href="#user-profile-update" class="md-nav__link"> <span class="md-ellipsis"> User profile update </span> </a> </li> <li class="md-nav__item"> <a href="#auto-confirm-user" class="md-nav__link"> <span class="md-ellipsis"> Auto-confirm user </span> </a> </li> <li class="md-nav__item"> <a href="#on-the-precedence-mask" class="md-nav__link"> <span class="md-ellipsis"> On the precedence mask </span> </a> </li> <li class="md-nav__item"> <a href="#defining-post-logout-url" class="md-nav__link"> <span class="md-ellipsis"> Defining post logout url </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item"> <a href="#security" class="md-nav__link"> <span class="md-ellipsis"> Security </span> </a> </li> <li class="md-nav__item"> <a href="#advanced-integrations" class="md-nav__link"> <span class="md-ellipsis"> Advanced integrations </span> </a> <nav class="md-nav" aria-label="Advanced integrations"> <ul class="md-nav__list"> <li class="md-nav__item"> <a href="#custom-login-page" class="md-nav__link"> <span class="md-ellipsis"> Custom login page </span> </a> </li> <li class="md-nav__item"> <a href="#custom-user-registration-form" class="md-nav__link"> <span class="md-ellipsis"> Custom user registration form </span> </a> </li> <li class="md-nav__item"> <a href="#custom-user-info" class="md-nav__link"> <span class="md-ellipsis"> Custom user info </span> </a> </li> <li class="md-nav__item"> <a href="#new-oauth-plugins" class="md-nav__link"> <span class="md-ellipsis"> New OAuth plugins </span> </a> </li> <li class="md-nav__item"> <a href="#allowdeny-user-login" class="md-nav__link"> <span class="md-ellipsis"> Allow/deny user login </span> </a> </li> <li class="md-nav__item"> <a href="#saml-integration" class="md-nav__link"> <span class="md-ellipsis"> SAML integration </span> </a> <nav class="md-nav" aria-label="SAML integration"> <ul class="md-nav__list"> <li class="md-nav__item"> <a href="#prerequisites" class="md-nav__link"> <span class="md-ellipsis"> Prerequisites </span> </a> </li> <li class="md-nav__item"> <a href="#server-information" class="md-nav__link"> <span class="md-ellipsis"> Server information </span> </a> </li> <li class="md-nav__item"> <a href="#configuration_1" class="md-nav__link"> <span class="md-ellipsis"> Configuration </span> </a> </li> <li class="md-nav__item"> <a href="#automatically-confirm-users" class="md-nav__link"> <span class="md-ellipsis"> Automatically confirm users </span> </a> <nav class="md-nav" aria-label="Automatically confirm users"> <ul class="md-nav__list"> <li class="md-nav__item"> <a href="#example-configurations-element" class="md-nav__link"> <span class="md-ellipsis"> Example configurations element </span> </a> </li> <li class="md-nav__item"> <a href="#troubleshooting-saml-configuration" class="md-nav__link"> <span class="md-ellipsis"> Troubleshooting SAML configuration </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item"> <a href="#show-the-login-button" class="md-nav__link"> <span class="md-ellipsis"> Show the login button </span> </a> </li> <li class="md-nav__item"> <a href="#multiple-saml-authentication-providers" class="md-nav__link"> <span class="md-ellipsis"> Multiple SAML authentication providers </span> </a> </li> </ul> </nav> </li> <li class="md-nav__item"> <a href="#groups" class="md-nav__link"> <span class="md-ellipsis"> Groups </span> </a> <nav class="md-nav" aria-label="Groups"> <ul class="md-nav__list"> <li class="md-nav__item"> <a href="#add-groups" class="md-nav__link"> <span class="md-ellipsis"> Add groups </span> </a> </li> <li class="md-nav__item"> <a href="#assign-groups-on-login" class="md-nav__link"> <span class="md-ellipsis"> Assign groups on login </span> </a> </li> </ul> </nav> </li> </ul> </nav> </li> </ul> </nav> </div> </div> </div> <div class="md-content" data-md-component="content"> <article class="md-content__inner md-typeset"> <h1 id="authentication">Authentication<a class="headerlink" href="#authentication" title="Permanent link">&para;</a></h1> <p>InvenioRDM supports local authentication only, integration with your institutional authentication system only (e.g. OAuth, SAML, Single Sign-on) or both at the same time.</p> <h2 id="local-authentication">Local Authentication<a class="headerlink" href="#local-authentication" title="Permanent link">&para;</a></h2> <p>By default, only the local authentication system is enabled. Users can create a new account using the registration form and activate their account after the email confirmation. These defaults are set in <code>invenio.cfg</code>:</p> <div class="highlight"><pre><span></span><code><span class="n">ACCOUNTS_LOCAL_LOGIN_ENABLED</span> <span class="o">=</span> <span class="kc">True</span> <span class="c1"># enable local login</span> <span class="n">SECURITY_REGISTERABLE</span> <span class="o">=</span> <span class="kc">True</span> <span class="c1"># local login: allow users to register</span> <span class="n">SECURITY_RECOVERABLE</span> <span class="o">=</span> <span class="kc">True</span> <span class="c1"># local login: allow users to reset the password</span> <span class="n">SECURITY_CHANGEABLE</span> <span class="o">=</span> <span class="kc">True</span> <span class="c1"># local login: allow users to change password</span> <span class="n">SECURITY_CONFIRMABLE</span> <span class="o">=</span> <span class="kc">True</span> <span class="c1"># require users to confirm their e-mail address</span> </code></pre></div> <h3 id="disabling-local-authentication">Disabling local authentication<a class="headerlink" href="#disabling-local-authentication" title="Permanent link">&para;</a></h3> <p>You can disable local authentication by modifying the setting in <code>invenio.cfg</code>:</p> <div class="highlight"><pre><span></span><code><span class="n">ACCOUNTS_LOCAL_LOGIN_ENABLED</span> <span class="o">=</span> <span class="kc">False</span> <span class="c1"># disable local login</span> </code></pre></div> <p>You typically disable local authentication when you want authentication to be handled by an external provider. As such, when local login is disabled, <strong>it is recommended</strong> to disable local account registration and password change/recovery:</p> <div class="highlight"><pre><span></span><code><span class="n">SECURITY_REGISTERABLE</span> <span class="o">=</span> <span class="kc">False</span> <span class="c1"># deny local registration of new users</span> <span class="n">SECURITY_RECOVERABLE</span> <span class="o">=</span> <span class="kc">False</span> <span class="c1"># deny resetting the local account&#39;s password</span> <span class="n">SECURITY_CHANGEABLE</span> <span class="o">=</span> <span class="kc">False</span> <span class="c1"># deny changing passwords for local accounts</span> </code></pre></div> <p>This way the external provider handles account management. <code>SECURITY_CONFIRMABLE</code> is kept <code>True</code> to check that the user can be reached via a valid email.</p> <div class="admonition warning"> <p class="admonition-title">Make sure that your configuration is consistent!</p> <p>While <em>some</em> clearly inconsistent configurations (e.g. having <code>SECURITY_REGISTERABLE=True</code> and at the same time <code>ACCOUNTS_LOCAL_LOGIN_ENABLED=False</code>) will trigger a console <strong>warning</strong> when running the Invenio webserver, other inconsistent combinations may be silently accepted and ultimately result in unexpected behaviors.</p> </div> <h2 id="external-authentication">External authentication<a class="headerlink" href="#external-authentication" title="Permanent link">&para;</a></h2> <p>InvenioRDM supports external authentication out-of-the-box, such as OAuth. SAML authentication can also be experimentally enabled.</p> <p>Note that the redirect url (or <em>authorized</em>) has the format <code>https://&lt;CFG_SITE_URL&gt;/oauth/authorized/&lt;contrib&gt;/</code>, where contrib can be <em>orcid</em>, <em>cern</em>, <em>github</em>, etc.</p> <h3 id="oauth">OAuth<a class="headerlink" href="#oauth" title="Permanent link">&para;</a></h3> <p>In addition to local accounts, InvenioRDM offers the possibility to integrate external <a href="https://oauth.net/2/">OAuth 2</a> / <a href="https://openid.net/connect/">OpenID Connect</a> authentication services via the <a href="https://invenio-oauthclient.readthedocs.io/en/latest/">Invenio-OAuthClient</a> module.</p> <h4 id="orcid">ORCID<a class="headerlink" href="#orcid" title="Permanent link">&para;</a></h4> <p>After having registered a new client application in the ORCID website and having set as <code>authorized</code> URL <code>https://&lt;CFG_SITE_URL&gt;/oauth/authorized/orcid/</code>, you can enable ORCID login in InvenioRDM by enabling the plugin and configuring key and secret. In your <code>invenio.cfg</code>:</p> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">invenio_oauthclient.contrib.orcid</span> <span class="kn">import</span> <span class="n">REMOTE_APP</span> <span class="k">as</span> <span class="n">ORCID_REMOTE_APP</span> <span class="n">OAUTHCLIENT_REMOTE_APPS</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span> <span class="n">orcid</span><span class="o">=</span><span class="n">ORCID_REMOTE_APP</span><span class="p">,</span> <span class="p">)</span> <span class="n">ORCID_APP_CREDENTIALS</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span> <span class="n">consumer_key</span><span class="o">=</span><span class="s2">&quot;&lt;my-key&gt;&quot;</span><span class="p">,</span> <span class="n">consumer_secret</span><span class="o">=</span><span class="s2">&quot;&lt;my-secret&gt;&quot;</span><span class="p">,</span> <span class="p">)</span> </code></pre></div> <p>You can also test ORCID login using the ORCID sandbox environment. See the plugin <a href="https://invenio-oauthclient.readthedocs.io/en/latest/usage.html#module-invenio_oauthclient.contrib.orcid">documentation</a> for more information.</p> <h4 id="github">GITHUB<a class="headerlink" href="#github" title="Permanent link">&para;</a></h4> <p>To integrate GitHub authentication into InvenioRDM, follow the steps below:</p> <ul> <li>Register a New GitHub Application by Navigating to GitHub's <a href="https://github.com/settings/applications/new">New OAuth Application page</a> to create a new OAuth application. Provide the necessary details with special attention to the <code>Homepage URL</code> and <code>Authorization callback URL</code>, which should match your InvenioRDM instance's callback URL format:</li> </ul> <div class="highlight"><pre><span></span><code><span class="na">Homepage URL</span><span class="o">:</span><span class="w"> </span><span class="s">https://127.0.0.1</span> <span class="na">Authorization callback URL</span><span class="o">:</span><span class="w"> </span><span class="s">https://127.0.0.1/oauth/authorized/github</span> </code></pre></div> <p>Change <code>https://127.0.0.1</code> to <code>SITE_UI_URL</code> in the <code>invenio.cfg</code> file to reflect your actual site URL.</p> <p>After registration, GitHub will provide a <code>Client ID</code> and <code>Client Secret</code>. These credentials are essential for the OAuth integration.</p> <ul> <li>Configure GitHub login in InvenioRDM by enabling the plugin and add the key and secret. In your <code>invenio.cfg</code>:</li> </ul> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">invenio_oauthclient.contrib.github</span> <span class="kn">import</span> <span class="n">REMOTE_APP</span> <span class="k">as</span> <span class="n">GITHUB_REMOTE_APP</span> <span class="c1"># Setup Github Authentication</span> <span class="c1"># ===========================</span> <span class="n">GITHUB_APP_CREDENTIALS</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span> <span class="n">consumer_key</span><span class="o">=</span><span class="s2">&quot;&lt;my-key&gt;&quot;</span><span class="p">,</span> <span class="n">consumer_secret</span><span class="o">=</span><span class="s2">&quot;&lt;my-secret&gt;&quot;</span><span class="p">,</span> <span class="p">)</span> <span class="n">OAUTHCLIENT_REMOTE_APPS</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span> <span class="n">github</span><span class="o">=</span><span class="n">GITHUB_REMOTE_APP</span><span class="p">,</span> <span class="p">)</span> </code></pre></div> <p>If you encounter any issues, or if a <code>302</code> appears in your logs, seek help through this <a href="https://docs.github.com/en/apps/oauth-apps/maintaining-oauth-apps/troubleshooting-authorization-request-errors">resource</a> or join the discussion on <a href="https://discord.gg/8qatqBC">discord</a> for support.</p> <h4 id="keycloak">Keycloak<a class="headerlink" href="#keycloak" title="Permanent link">&para;</a></h4> <p><a href="https://www.keycloak.org/">Keycloak</a> is an open-source solution for identity management that can be used for single sign-on endpoints.</p> <h5 id="configuration">Configuration<a class="headerlink" href="#configuration" title="Permanent link">&para;</a></h5> <p>Information required to configure the InvenioRDM instance:</p> <ul> <li>The base URL (including port) of the Keycloak server.</li> <li>Information about the client, as configured in Keycloak:<ul> <li>Its realm.</li> <li>The client ID.</li> <li>The client secret.</li> <li>The target audience of Keycloak's JWTs (probably the same as the client ID).</li> </ul> </li> </ul> <div class="admonition note"> <p class="admonition-title">Note</p> <p>The client configuration in Keycloak server must have its access type set to <em>confidential</em> and use the client authenticator mechanism <em>client ID and secret</em>.</p> </div> <p>In your <code>invenio.cfg</code>:</p> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">invenio_oauthclient.contrib.keycloak</span> <span class="kn">import</span> <span class="n">KeycloakSettingsHelper</span> <span class="n">helper</span> <span class="o">=</span> <span class="n">KeycloakSettingsHelper</span><span class="p">(</span> <span class="n">title</span><span class="o">=</span><span class="s2">&quot;Keycloak&quot;</span><span class="p">,</span> <span class="n">description</span><span class="o">=</span><span class="s2">&quot;Keycloak Authentication Service&quot;</span><span class="p">,</span> <span class="n">base_url</span><span class="o">=</span><span class="s2">&quot;https://myserver.com:4430&quot;</span><span class="p">,</span> <span class="n">realm</span><span class="o">=</span><span class="s2">&quot;my-realm&quot;</span> <span class="p">)</span> <span class="n">OAUTHCLIENT_KEYCLOAK_REALM_URL</span> <span class="o">=</span> <span class="n">helper</span><span class="o">.</span><span class="n">realm_url</span> <span class="n">OAUTHCLIENT_KEYCLOAK_USER_INFO_URL</span> <span class="o">=</span> <span class="n">helper</span><span class="o">.</span><span class="n">user_info_url</span> <span class="n">OAUTHCLIENT_KEYCLOAK_VERIFY_EXP</span> <span class="o">=</span> <span class="kc">True</span> <span class="c1"># whether to verify the expiration date of tokens</span> <span class="n">OAUTHCLIENT_KEYCLOAK_VERIFY_AUD</span> <span class="o">=</span> <span class="kc">True</span> <span class="c1"># whether to verify the audience tag for tokens</span> <span class="n">OAUTHCLIENT_KEYCLOAK_AUD</span> <span class="o">=</span> <span class="s2">&quot;&lt;YOUR.AUDIENCE&gt;&quot;</span> <span class="c1"># probably the same as the client ID</span> <span class="n">OAUTHCLIENT_REMOTE_APPS</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">&quot;keycloak&quot;</span><span class="p">:</span> <span class="n">helper</span><span class="o">.</span><span class="n">remote_app</span><span class="p">,</span> <span class="p">}</span> <span class="n">KEYCLOAK_APP_CREDENTIALS</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">&quot;consumer_key&quot;</span><span class="p">:</span> <span class="s2">&quot;&lt;YOUR.CLIENT.ID&gt;&quot;</span><span class="p">,</span> <span class="s2">&quot;consumer_secret&quot;</span><span class="p">:</span> <span class="s2">&quot;&lt;YOUR.CLIENT.CREDENTIALS.SECRET&gt;&quot;</span><span class="p">,</span> <span class="p">}</span> </code></pre></div> <h5 id="tweaking-configuration">Tweaking configuration<a class="headerlink" href="#tweaking-configuration" title="Permanent link">&para;</a></h5> <p>The <code>KeycloakSettingsHelper</code> is only there to make the configuration easier, since the base URL and realm are used several times in the <code>remote_app</code> dictionary. You can change many other settings of the Keycloak configuration in case you need more fine-grained control.</p> <p>Here is what the full configuration looks like:</p> <div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">helper</span><span class="o">.</span><span class="n">remote_app</span> <span class="p">{</span> <span class="s2">&quot;title&quot;</span><span class="p">:</span> <span class="s2">&quot;Keycloak&quot;</span><span class="p">,</span> <span class="s2">&quot;description&quot;</span><span class="p">:</span> <span class="s2">&quot;Keycloak Authentication Service&quot;</span><span class="p">,</span> <span class="s2">&quot;icon&quot;</span><span class="p">:</span> <span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="s2">&quot;authorized_handler&quot;</span><span class="p">:</span> <span class="s2">&quot;invenio_oauthclient.handlers:authorized_signup_handler&quot;</span><span class="p">,</span> <span class="s2">&quot;disconnect_handler&quot;</span><span class="p">:</span> <span class="s2">&quot;invenio_oauthclient.contrib.keycloak.handlers:disconnect_handler&quot;</span><span class="p">,</span> <span class="s2">&quot;signup_handler&quot;</span><span class="p">:</span> <span class="p">{</span> <span class="s2">&quot;info&quot;</span><span class="p">:</span> <span class="s2">&quot;invenio_oauthclient.contrib.keycloak.handlers:info_handler&quot;</span><span class="p">,</span> <span class="s2">&quot;setup&quot;</span><span class="p">:</span> <span class="s2">&quot;invenio_oauthclient.contrib.keycloak.handlers:setup_handler&quot;</span><span class="p">,</span> <span class="s2">&quot;view&quot;</span><span class="p">:</span> <span class="s2">&quot;invenio_oauthclient.handlers:signup_handler&quot;</span> <span class="p">},</span> <span class="s2">&quot;precedence_mask&quot;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&quot;email&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span> <span class="s2">&quot;profile&quot;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&quot;username&quot;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span> <span class="s2">&quot;full_name&quot;</span><span class="p">:</span> <span class="kc">False</span><span class="p">}},</span> <span class="s2">&quot;params&quot;</span><span class="p">:</span> <span class="p">{</span> <span class="s2">&quot;base_url&quot;</span><span class="p">:</span> <span class="s2">&quot;https://myserver.com:4430&quot;</span><span class="p">,</span> <span class="s2">&quot;request_token_params&quot;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&quot;scope&quot;</span><span class="p">:</span> <span class="s2">&quot;openid&quot;</span><span class="p">},</span> <span class="s2">&quot;request_token_url&quot;</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span> <span class="s2">&quot;access_token_url&quot;</span><span class="p">:</span> <span class="s2">&quot;https://myserver.com:4430/auth/realms/my-realm/protocol/openid-connect/token&quot;</span><span class="p">,</span> <span class="s2">&quot;access_token_method&quot;</span><span class="p">:</span> <span class="s2">&quot;POST&quot;</span><span class="p">,</span> <span class="s2">&quot;authorize_url&quot;</span><span class="p">:</span> <span class="s2">&quot;https://myserver.com:4430/auth/realms/my-realm/protocol/openid-connect/auth&quot;</span><span class="p">,</span> <span class="s2">&quot;app_key&quot;</span><span class="p">:</span> <span class="s2">&quot;KEYCLOAK_APP_CREDENTIALS&quot;</span> <span class="p">}</span> <span class="p">}</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">helper</span><span class="o">.</span><span class="n">realm_url</span> <span class="s2">&quot;https://myserver.com:4430/auth/realms/my-realm&quot;</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">helper</span><span class="o">.</span><span class="n">user_info_url</span> <span class="s2">&quot;https://myserver.com:4430/auth/realms/my-realm/protocol/openid-connect/userinfo&quot;</span> </code></pre></div> <div class="admonition note"> <p class="admonition-title">Note</p> <p>The <code>precedence_mask</code> dictates which information about the user (name, email, etc.) provided by authentication server should take precedence over any user input. For more information, see <a href="#on-the-precedence-mask">the related section</a>.</p> </div> <p>You can modify default configuration directly in your <code>invenio.cfg</code>:</p> <div class="highlight"><pre><span></span><code><span class="n">keycloak_remote_app</span> <span class="o">=</span> <span class="n">helper</span><span class="o">.</span><span class="n">remote_app</span> <span class="c1"># change the title that will be shown in the login button &quot;Login with My institute&quot;</span> <span class="n">keycloak_remote_app</span><span class="p">[</span><span class="s2">&quot;title&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&quot;My institute&quot;</span> <span class="c1"># change the name of the variable holding the credentials</span> <span class="n">keycloak_remote_app</span><span class="p">[</span><span class="s2">&quot;params&quot;</span><span class="p">][</span><span class="s2">&quot;app_key&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&quot;OAUTHCLIENT_ANOTHER_NAME_APP_CREDENTIALS&quot;</span> <span class="n">OAUTHCLIENT_ANOTHER_NAME_APP_CREDENTIALS</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">&quot;consumer_key&quot;</span><span class="p">:</span> <span class="s2">&quot;&lt;YOUR.CLIENT.ID&gt;&quot;</span><span class="p">,</span> <span class="s2">&quot;consumer_secret&quot;</span><span class="p">:</span> <span class="s2">&quot;&lt;YOUR.CLIENT.CREDENTIALS.SECRET&gt;&quot;</span><span class="p">,</span> <span class="p">}</span> <span class="c1"># update the remote apps configuration</span> <span class="n">OAUTHCLIENT_REMOTE_APPS</span><span class="p">[</span><span class="s2">&quot;keycloak&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">keycloak_remote_app</span> </code></pre></div> <h5 id="multiple-keycloak-authentication-providers">Multiple Keycloak authentication providers<a class="headerlink" href="#multiple-keycloak-authentication-providers" title="Permanent link">&para;</a></h5> <p>You might have the need to integrate multiple Keycloak authentication providers at the same time, to allow users to log in with one or the other. You can "namespace" each remote application using a different value for the parameter <code>app_key</code>. In your <code>invenio.cfg</code>:</p> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">invenio_oauthclient.contrib.keycloak</span> <span class="kn">import</span> <span class="n">KeycloakSettingsHelper</span> <span class="n">foo</span> <span class="o">=</span> <span class="n">KeycloakSettingsHelper</span><span class="p">(</span> <span class="n">title</span><span class="o">=</span><span class="s2">&quot;Foo provider&quot;</span><span class="p">,</span> <span class="n">description</span><span class="o">=</span><span class="s2">&quot;Keycloak Authentication foo provider&quot;</span><span class="p">,</span> <span class="n">base_url</span><span class="o">=</span><span class="s2">&quot;https://foo.com:4430&quot;</span><span class="p">,</span> <span class="n">realm</span><span class="o">=</span><span class="s2">&quot;foo-realm&quot;</span><span class="p">,</span> <span class="n">app_key</span><span class="o">=</span><span class="s2">&quot;FOO_KEYCLOAK_APP_CREDENTIALS&quot;</span> <span class="p">)</span> <span class="n">bar</span> <span class="o">=</span> <span class="n">KeycloakSettingsHelper</span><span class="p">(</span> <span class="n">title</span><span class="o">=</span><span class="s2">&quot;Bar provider&quot;</span><span class="p">,</span> <span class="n">description</span><span class="o">=</span><span class="s2">&quot;Keycloak Authentication bar provider&quot;</span><span class="p">,</span> <span class="n">base_url</span><span class="o">=</span><span class="s2">&quot;https://bar.com:4430&quot;</span><span class="p">,</span> <span class="n">realm</span><span class="o">=</span><span class="s2">&quot;bar-realm&quot;</span><span class="p">,</span> <span class="n">app_key</span><span class="o">=</span><span class="s2">&quot;BAR_KEYCLOAK_APP_CREDENTIALS&quot;</span> <span class="p">)</span> <span class="n">OAUTHCLIENT_REMOTE_APPS</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">&quot;foo_keycloak&quot;</span><span class="p">:</span> <span class="n">foo</span><span class="o">.</span><span class="n">remote_app</span><span class="p">,</span> <span class="s2">&quot;bar_keycloak&quot;</span><span class="p">:</span> <span class="n">bar</span><span class="o">.</span><span class="n">remote_app</span><span class="p">,</span> <span class="p">}</span> <span class="n">OAUTHCLIENT_FOO_KEYCLOAK_REALM_URL</span> <span class="o">=</span> <span class="n">foo</span><span class="o">.</span><span class="n">realm_url</span> <span class="n">OAUTHCLIENT_FOO_KEYCLOAK_USER_INFO_URL</span> <span class="o">=</span> <span class="n">foo</span><span class="o">.</span><span class="n">user_info_url</span> <span class="n">OAUTHCLIENT_FOO_KEYCLOAK_VERIFY_EXP</span> <span class="o">=</span> <span class="kc">True</span> <span class="n">OAUTHCLIENT_FOO_KEYCLOAK_VERIFY_AUD</span> <span class="o">=</span> <span class="kc">True</span> <span class="n">OAUTHCLIENT_FOO_KEYCLOAK_AUD</span> <span class="o">=</span> <span class="s2">&quot;&lt;FOO.AUDIENCE&gt;&quot;</span> <span class="n">OAUTHCLIENT_BAR_KEYCLOAK_REALM_URL</span> <span class="o">=</span> <span class="n">bar</span><span class="o">.</span><span class="n">realm_url</span> <span class="n">OAUTHCLIENT_BAR_KEYCLOAK_USER_INFO_URL</span> <span class="o">=</span> <span class="n">bar</span><span class="o">.</span><span class="n">user_info_url</span> <span class="n">OAUTHCLIENT_BAR_KEYCLOAK_VERIFY_EXP</span> <span class="o">=</span> <span class="kc">False</span> <span class="n">OAUTHCLIENT_BAR_KEYCLOAK_VERIFY_AUD</span> <span class="o">=</span> <span class="kc">True</span> <span class="n">OAUTHCLIENT_BAR_KEYCLOAK_AUD</span> <span class="o">=</span> <span class="s2">&quot;&lt;BAR.AUDIENCE&gt;&quot;</span> <span class="n">FOO_KEYCLOAK_APP_CREDENTIALS</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">&quot;consumer_key&quot;</span><span class="p">:</span> <span class="s2">&quot;&lt;FOO.CLIENT.ID&gt;&quot;</span><span class="p">,</span> <span class="s2">&quot;consumer_secret&quot;</span><span class="p">:</span> <span class="s2">&quot;&lt;FOO.CLIENT.CREDENTIALS.SECRET&gt;&quot;</span><span class="p">,</span> <span class="p">}</span> <span class="n">BAR_KEYCLOAK_APP_CREDENTIALS</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">&quot;consumer_key&quot;</span><span class="p">:</span> <span class="s2">&quot;&lt;BAR.CLIENT.ID&gt;&quot;</span><span class="p">,</span> <span class="s2">&quot;consumer_secret&quot;</span><span class="p">:</span> <span class="s2">&quot;&lt;BAR.CLIENT.CREDENTIALS.SECRET&gt;&quot;</span><span class="p">,</span> <span class="p">}</span> </code></pre></div> <div class="admonition warning"> <p class="admonition-title">Naming convention</p> <p>Notice how the <code>app_key</code> param must match with the name of the config <code>&lt;APP_KEY&gt;_APP_CREDENTIALS</code>, uppercase, and the name of the apps in the config <code>OAUTHCLIENT_REMOTE_APPS</code> (<code>foo_keycloak</code>, <code>bar_keycloak</code>) must match with the related config vars <code>OAUTHCLIENT_FOO_KEYCLOAK_*</code> and <code>OAUTHCLIENT_BAR_KEYCLOAK_*</code>, uppercase.</p> </div> <h4 id="openaire-aai">OpenAIRE AAI<a class="headerlink" href="#openaire-aai" title="Permanent link">&para;</a></h4> <p>Register a new application with OpenAIRE, by sending an email to <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#97;&#97;&#105;&#64;&#111;&#112;&#101;&#110;&#97;&#105;&#114;&#101;&#46;&#101;&#117;">&#97;&#97;&#105;&#64;&#111;&#112;&#101;&#110;&#97;&#105;&#114;&#101;&#46;&#101;&#117;</a>, with the following information:</p> <ul> <li>Client ID for your application (e.g. <code>my-app</code>).</li> <li>One or more <em>Redirect URI</em>s pointing to <code>https://&lt;host&gt;/oauth/authorized/openaire_aai/</code> ( e.g. <code>https://localhost:5000/oauth/authorized/openaire_aai/</code>).</li> <li>User claim scopes <code>openid profile email orcid</code>.</li> <li>One or more of the OpenID Connect/OAuth2 grant types: Authorization Code, Token Exchange, Device Code.</li> <li>Optionally, you can request to also register an application for the OpenAIRE AAI sandbox instance for testing.</li> </ul> <p>After registering your application, you can enable OpenAIRE login in InvenioRDM by enabling the plugin and configuring the key and secret. In your <code>invenio.cfg</code>:</p> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">invenio_oauthclient.contrib</span> <span class="kn">import</span> <span class="n">openaire_aai</span> <span class="n">OAUTHCLIENT_REMOTE_APPS</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span> <span class="n">openaire_aai</span><span class="o">=</span><span class="n">openaire_aai</span><span class="o">.</span><span class="n">REMOTE_APP</span><span class="p">,</span> <span class="p">)</span> <span class="n">OPENAIRE_APP_CREDENTIALS</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span> <span class="n">consumer_key</span><span class="o">=</span><span class="s2">&quot;changeme&quot;</span><span class="p">,</span> <span class="n">consumer_secret</span><span class="o">=</span><span class="s2">&quot;changeme&quot;</span><span class="p">,</span> <span class="p">)</span> </code></pre></div> <p>In case you want to use the sandbox environment, use <code>openaire_aai.REMOTE_SANDBOX_APP</code> instead of <code>openaire_aai.REMOTE_APP</code>.</p> <h3 id="login-automatic-redirection">Login automatic redirection<a class="headerlink" href="#login-automatic-redirection" title="Permanent link">&para;</a></h3> <p>When local login is disabled and there is exactly one OAuthClient remote app defined, the login page can be skipped and the user can be redirected directly to the external authentication provider. In your <code>invenio.cfg</code>:</p> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">invenio_oauthclient.views.client</span> <span class="kn">import</span> <span class="n">auto_redirect_login</span> <span class="n">ACCOUNTS_LOGIN_VIEW_FUNCTION</span> <span class="o">=</span> <span class="n">auto_redirect_login</span> <span class="n">OAUTHCLIENT_AUTO_REDIRECT_TO_EXTERNAL_LOGIN</span> <span class="o">=</span> <span class="kc">True</span> </code></pre></div> <p>This automatic redirection will not work, even if configured as above, if local login is enabled or you have configured multiple remote applications.</p> <h3 id="user-profile-update">User profile update<a class="headerlink" href="#user-profile-update" title="Permanent link">&para;</a></h3> <p>You might want to disable the update of the user profile (email, full name, etc.) when only external authentication is enabled. You can set the user profile form as read-only by changing in your <code>invenio.cfg</code>:</p> <div class="highlight"><pre><span></span><code><span class="n">USERPROFILES_READ_ONLY</span> <span class="o">=</span> <span class="kc">True</span> </code></pre></div> <h3 id="auto-confirm-user">Auto-confirm user<a class="headerlink" href="#auto-confirm-user" title="Permanent link">&para;</a></h3> <p><em>Introduced in InvenioRDM v11</em></p> <p>By default, users who log in using an external authentication provider are <code>confirmed</code> and the e-mail confirmation is not sent anymore.</p> <p>The only exception is the ORCID OAuth plugin: the user e-mail cannot be retrieved by this provider and the user must provide it when registering for the first time. In this case, the registration follows the classic flow, with e-mail confirmation.</p> <p>You can modify this behavior per plugin:</p> <div class="highlight"><pre><span></span><code>_keycloak_helper = KeycloakSettingsHelper( <span class="w"> </span> title=&quot;CERN&quot;, <span class="w"> </span> description=&quot;CERN SSO authentication&quot;, <span class="w"> </span> ... <span class="gi">+ signup_options=dict(</span> <span class="gi">+ auto_confirm=True,</span> <span class="gi">+ send_register_msg=False,</span> <span class="gi">+ ),</span> ) </code></pre></div> <h3 id="on-the-precedence-mask">On the precedence mask<a class="headerlink" href="#on-the-precedence-mask" title="Permanent link">&para;</a></h3> <p>On first user login after external authentication flow, the user information are fetched from the authentication provider or they might be input by the user via a <a href="#custom-user-registration-form">custom registration form</a>.</p> <p>You can enforce what user information should be taken from the server and cannot be overridden by the user during registration, so that you are sure what information to trust. In your OAuth application, you can fine tune the <code>precedence_mask</code>:</p> <div class="highlight"><pre><span></span><code><span class="n">remote_app</span><span class="p">[</span><span class="s2">&quot;precedence_mask&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;email&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span> <span class="s2">&quot;profile&quot;</span><span class="p">:</span> <span class="p">{</span><span class="s2">&quot;username&quot;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span> <span class="s2">&quot;full_name&quot;</span><span class="p">:</span> <span class="kc">False</span><span class="p">}}</span> </code></pre></div> <p>Properties marked with <code>True</code> (or omitted) in the precedence mask will be taken from the authentication server user information payload if available, while properties marked with <code>False</code> will be taken from the user input in the registration form.</p> <h3 id="defining-post-logout-url">Defining post logout url<a class="headerlink" href="#defining-post-logout-url" title="Permanent link">&para;</a></h3> <p>By default, upon logging out, the application will disconnect you only from itself. However, if you logged in through an external provider, logging out from the application will not automatically log you out from that provider. To address this, you can define a <code>logout_url</code> when setting up the remote apps:</p> <div class="highlight"><pre><span></span><code>_keycloak_helper = KeycloakSettingsHelper( <span class="w"> </span> title=&quot;CERN&quot;, <span class="w"> </span> description=&quot;CERN SSO authentication&quot;, <span class="w"> </span> ... <span class="gi">+ logout_url=&quot;your_app/logout&quot;,</span> <span class="w"> </span> ), ) </code></pre></div> <p>After setting the <code>logout_url</code>, it is necessary to include the following configuration variable:</p> <div class="highlight"><pre><span></span><code><span class="n">SECURITY_POST_LOGOUT_VIEW</span> <span class="o">=</span> <span class="s2">&quot;/oauth/logout&quot;</span> <span class="sd">&quot;&quot;&quot;Required by invenio-oauthclient to be able to set logout urls for the remote apps.&quot;&quot;&quot;</span> </code></pre></div> <p>This will redirect to the appropriate <code>logout_url</code> for each of the enabled remotes in the instance.</p> <h2 id="security">Security<a class="headerlink" href="#security" title="Permanent link">&para;</a></h2> <p>For increased security, you should define the following in your <code>invenio.cfg</code>:</p> <div class="highlight"><pre><span></span><code><span class="n">SECURITY_CONFIRMABLE</span> <span class="o">=</span> <span class="kc">True</span> <span class="c1"># enable e-mail address confirmation for local login</span> <span class="n">SECURITY_TRACKABLE</span> <span class="o">=</span> <span class="kc">True</span> <span class="c1"># enable tracking of basic user login statistics</span> <span class="n">SECURITY_PASSWORD_SALT</span> <span class="o">=</span> <span class="s2">&quot;..put a long random value here..&quot;</span> <span class="n">SECURITY_CONFIRM_SALT</span> <span class="o">=</span> <span class="s2">&quot;..put a long random value here..&quot;</span> <span class="n">SECURITY_RESET_SALT</span> <span class="o">=</span> <span class="s2">&quot;..put a long random value here..&quot;</span> <span class="n">SECURITY_LOGIN_SALT</span> <span class="o">=</span> <span class="s2">&quot;..put a long random value here..&quot;</span> <span class="n">SECURITY_CHANGE_SALT</span> <span class="o">=</span> <span class="s2">&quot;..put a long random value here..&quot;</span> <span class="n">SECURITY_REMEMBER_SALT</span> <span class="o">=</span> <span class="s2">&quot;..put a long random value here..&quot;</span> </code></pre></div> <p>Additional configuration items for security, sending and customising account registration-related emails, etc. can be found in the <a href="https://flask-security.readthedocs.io/en/latest/configuration.html">documentation for Flask-Security</a>.</p> <p>You can change the default duration (31 days) of the logged in user session (the session cookie expiration). This is particularly useful when configuring InvenioRDM with external authentication only and you want to have the same session duration as the external authentication provider.</p> <p>In your <code>invenio.cfg</code>:</p> <div class="highlight"><pre><span></span><code><span class="n">PERMANENT_SESSION_LIFETIME</span> <span class="o">=</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span> </code></pre></div> <h2 id="advanced-integrations">Advanced integrations<a class="headerlink" href="#advanced-integrations" title="Permanent link">&para;</a></h2> <h3 id="custom-login-page">Custom login page<a class="headerlink" href="#custom-login-page" title="Permanent link">&para;</a></h3> <p>You can customize the login page template to display different information or change its look and feel.</p> <p>Start from an existing template:</p> <ul> <li>If you have local login only, copy the folder <a href="https://github.com/inveniosoftware/invenio-accounts/tree/master/invenio_accounts/templates/semantic-ui">templates/semantic-ui</a> from <code>invenio-accounts</code>.</li> <li>If you have external authentication, copy the folder <a href="https://github.com/inveniosoftware/invenio-oauthclient/tree/master/invenio_oauthclient/templates/semantic-ui">templates/semantic-ui</a> from <code>invenio-oauthclient</code>.</li> </ul> <p>Then, open the <code>templates</code> folder in <code>my-site</code> (your instance) and paste it there. Inside the <code>invenio-accounts</code>/<code>invenio-oauthclient</code> folder, keep only the template file that you want to customize.</p> <p>Edit the Jinja template as you need.</p> <p>For more information, see the guide <a href="../look-and-feel/templates/">style other pages</a>.</p> <h3 id="custom-user-registration-form">Custom user registration form<a class="headerlink" href="#custom-user-registration-form" title="Permanent link">&para;</a></h3> <p>Whenever a new user logs in to InvenioRDM with external authentication for the first time, Invenio will create a local account associated with the external account.</p> <p>The local account user information, such as e-mail, username and full name are normally fetched when authenticating and automatically filled in. In case of ORCID login, the user e-mail cannot be retrieved when authenticating and a registration form will be prompted to the user asking to fill in the e-mail.</p> <p>You can customize such behavior and allow users to modify the information coming from the authentication server or require extra user input (e.g. require the user to accept website terms and conditions).</p> <p>The following example configuration creates different registration forms for different external authentication providers. All of them have an extra checkbox for accepting the website's terms and conditions, which is mandatory to complete the registration:</p> <p><code>my_package/registration_form.py</code>:</p> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">invenio_oauthclient.utils</span> <span class="kn">import</span> <span class="n">create_registrationform</span> <span class="kn">from</span> <span class="nn">invenio_userprofiles.forms</span> <span class="kn">import</span> <span class="n">ProfileForm</span> <span class="kn">from</span> <span class="nn">wtforms</span> <span class="kn">import</span> <span class="n">BooleanField</span><span class="p">,</span> <span class="n">validators</span><span class="p">,</span> <span class="n">FormField</span> <span class="kn">from</span> <span class="nn">werkzeug.local</span> <span class="kn">import</span> <span class="n">LocalProxy</span> <span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">current_app</span><span class="p">,</span> <span class="n">url_for</span> <span class="kn">from</span> <span class="nn">markupsafe</span> <span class="kn">import</span> <span class="n">Markup</span> <span class="n">_security</span> <span class="o">=</span> <span class="n">LocalProxy</span><span class="p">(</span><span class="k">lambda</span><span class="p">:</span> <span class="n">current_app</span><span class="o">.</span><span class="n">extensions</span><span class="p">[</span><span class="s1">&#39;security&#39;</span><span class="p">])</span> <span class="k">def</span> <span class="nf">my_registration_form</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> <span class="c1"># create a link to a PDF file containing the terms and conditions</span> <span class="c1"># the PDF file is located in the `static/documents` folder.</span> <span class="n">terms_of_use_url</span> <span class="o">=</span> <span class="n">url_for</span><span class="p">(</span><span class="s2">&quot;static&quot;</span><span class="p">,</span> <span class="n">filename</span><span class="o">=</span><span class="p">(</span><span class="s2">&quot;documents/terms-of-use.pdf&quot;</span><span class="p">))</span> <span class="n">terms_of_use_text</span> <span class="o">=</span> <span class="n">Markup</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Accept the &lt;a href=&#39;</span><span class="si">{</span><span class="n">terms_of_use_url</span><span class="si">}</span><span class="s2">&#39; target=&#39;_blank&#39;&gt;terms and conditions&lt;/a&gt;&quot;</span><span class="p">)</span> <span class="n">current_remote_app</span> <span class="o">=</span> <span class="n">kwargs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;oauth_remote_app&quot;</span><span class="p">)</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">current_remote_app</span><span class="p">:</span> <span class="c1"># return default just in case something is wrong</span> <span class="k">return</span> <span class="n">create_registrationform</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="c1"># optionally, have different registration forms depending on the authentication</span> <span class="c1"># provider used by the user to login</span> <span class="k">elif</span> <span class="n">current_remote_app</span><span class="o">.</span><span class="n">name</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="o">!=</span> <span class="s2">&quot;orcid&quot;</span><span class="p">:</span> <span class="c1"># show this form in case the user logged in with any method but ORCID</span> <span class="k">class</span> <span class="nc">DefaultRegistrationForm</span><span class="p">(</span><span class="n">_security</span><span class="o">.</span><span class="n">confirm_register_form</span><span class="p">):</span> <span class="n">email</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># remove the email field</span> <span class="n">password</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># remove the password field</span> <span class="n">profile</span> <span class="o">=</span> <span class="n">FormField</span><span class="p">(</span><span class="n">ProfileForm</span><span class="p">,</span> <span class="n">separator</span><span class="o">=</span><span class="s2">&quot;.&quot;</span><span class="p">)</span> <span class="n">recaptcha</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># remove the captcha</span> <span class="n">submit</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># remove submit btn, already defined in the template</span> <span class="n">terms_of_use</span> <span class="o">=</span> <span class="n">BooleanField</span><span class="p">(</span><span class="n">terms_of_use_text</span><span class="p">,</span> <span class="p">[</span><span class="n">validators</span><span class="o">.</span><span class="n">required</span><span class="p">()])</span> <span class="c1"># add the new field</span> <span class="k">return</span> <span class="n">DefaultRegistrationForm</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="k">else</span><span class="p">:</span> <span class="c1"># ORCID does not provide the user e-mail address, it must be input by the user.</span> <span class="c1"># the email field comes from `confirm_register_form` upper class.</span> <span class="k">class</span> <span class="nc">OrcidRegistrationForm</span><span class="p">(</span><span class="n">_security</span><span class="o">.</span><span class="n">confirm_register_form</span><span class="p">):</span> <span class="n">password</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># remove the password field</span> <span class="n">profile</span> <span class="o">=</span> <span class="n">FormField</span><span class="p">(</span><span class="n">ProfileForm</span><span class="p">,</span> <span class="n">separator</span><span class="o">=</span><span class="s2">&quot;.&quot;</span><span class="p">)</span> <span class="n">recaptcha</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># remove the captcha</span> <span class="n">submit</span> <span class="o">=</span> <span class="kc">None</span> <span class="c1"># remove submit btn, already defined in the template</span> <span class="n">terms_of_use</span> <span class="o">=</span> <span class="n">BooleanField</span><span class="p">(</span><span class="n">terms_of_use_text</span><span class="p">,</span> <span class="p">[</span><span class="n">validators</span><span class="o">.</span><span class="n">required</span><span class="p">()])</span> <span class="c1"># add the new field</span> <span class="k">return</span> <span class="n">OrcidRegistrationForm</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> </code></pre></div> <p><code>invenio.cfg</code>:</p> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">my_package.registration_form</span> <span class="kn">import</span> <span class="n">my_registration_form</span> <span class="c1"># use the custom form function</span> <span class="n">OAUTHCLIENT_SIGNUP_FORM</span> <span class="o">=</span> <span class="n">my_registration_form</span> </code></pre></div> <h3 id="custom-user-info">Custom user info<a class="headerlink" href="#custom-user-info" title="Permanent link">&para;</a></h3> <p><em>Introduced in InvenioRDM v11</em></p> <p>You can customize how each OAuth plugin will fetch the user information on login, and what fields should be kept or how they should be serialized.</p> <p>You can customize the function that will be called to fetch the user information:</p> <div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">info_handler</span><span class="p">(</span><span class="n">remote</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;Retrieve remote account information.</span> <span class="sd"> :param remote: The remote application.</span> <span class="sd"> :param resp: The response of the `authorized` endpoint.</span> <span class="sd"> :returns: A dictionary with the user information.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">user_info</span> <span class="o">=</span> <span class="o">...</span> <span class="n">fetch</span> <span class="n">user</span> <span class="n">info</span> <span class="o">...</span> <span class="k">return</span> <span class="n">custom_info_serializer</span><span class="p">(</span><span class="n">user_info</span><span class="p">)</span> <span class="n">_keycloak_helper</span> <span class="o">=</span> <span class="n">KeycloakSettingsHelper</span><span class="p">(</span><span class="o">...</span><span class="p">)</span> <span class="n">handlers</span> <span class="o">=</span> <span class="n">_keycloak_helper</span><span class="o">.</span><span class="n">get_handlers</span><span class="p">()</span> <span class="n">handlers</span><span class="p">[</span><span class="s2">&quot;signup_handler&quot;</span><span class="p">][</span><span class="s2">&quot;info&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">custom_info_serializer</span> </code></pre></div> <p>You can customize the function that will be called to serialize the user information:</p> <div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">custom_info_serializer</span><span class="p">(</span><span class="n">remote</span><span class="p">,</span> <span class="n">resp</span><span class="p">,</span> <span class="n">user_info</span><span class="p">):</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;Serialize the account info response object.</span> <span class="sd"> :param remote: The remote application.</span> <span class="sd"> :param resp: The response of the `authorized` endpoint.</span> <span class="sd"> :param user_info: The response of the `user info` endpoint.</span> <span class="sd"> :returns: A dictionary with serialized user information.</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="k">return</span> <span class="p">{</span> <span class="s2">&quot;user&quot;</span><span class="p">:</span> <span class="p">{</span> <span class="s2">&quot;active&quot;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span> <span class="s2">&quot;email&quot;</span><span class="p">:</span> <span class="n">user_info</span><span class="p">[</span><span class="s2">&quot;email&quot;</span><span class="p">],</span> <span class="s2">&quot;profile&quot;</span><span class="p">:</span> <span class="p">{</span> <span class="s2">&quot;full_name&quot;</span><span class="p">:</span> <span class="n">user_info</span><span class="p">[</span><span class="s2">&quot;name&quot;</span><span class="p">],</span> <span class="s2">&quot;username&quot;</span><span class="p">:</span> <span class="n">user_info</span><span class="p">[</span><span class="s2">&quot;preferred_username&quot;</span><span class="p">],</span> <span class="p">},</span> <span class="s2">&quot;prefs&quot;</span><span class="p">:</span> <span class="p">{</span> <span class="s2">&quot;visibility&quot;</span><span class="p">:</span> <span class="s2">&quot;restricted&quot;</span><span class="p">,</span> <span class="s2">&quot;email_visibility&quot;</span><span class="p">:</span> <span class="s2">&quot;restricted&quot;</span><span class="p">,</span> <span class="p">},</span> <span class="p">},</span> <span class="s2">&quot;external_id&quot;</span><span class="p">:</span> <span class="n">user_info</span><span class="p">[</span><span class="s2">&quot;upn&quot;</span><span class="p">],</span> <span class="s2">&quot;external_method&quot;</span><span class="p">:</span> <span class="n">remote</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="p">}</span> <span class="n">_keycloak_helper</span> <span class="o">=</span> <span class="n">KeycloakSettingsHelper</span><span class="p">(</span><span class="o">...</span><span class="p">)</span> <span class="n">handlers</span> <span class="o">=</span> <span class="n">_keycloak_helper</span><span class="o">.</span><span class="n">get_handlers</span><span class="p">()</span> <span class="n">handlers</span><span class="p">[</span><span class="s2">&quot;signup_handler&quot;</span><span class="p">][</span><span class="s2">&quot;info_serializer&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">custom_info_serializer</span> </code></pre></div> <div class="admonition info"> <p class="admonition-title">Mandatory external id</p> <p>When customizing an info endpoint, it is mandatory for a field to provide an external ID. This field must be named <code>external_id</code>.</p> </div> <h3 id="new-oauth-plugins">New OAuth plugins<a class="headerlink" href="#new-oauth-plugins" title="Permanent link">&para;</a></h3> <p>If you need to implement your own OAuth plugin to enable integration with your OAuth provider, you can take advantage of the available OAuth helper class. You will need anyway to implement some authentication handlers depending on your server authentication workflow. You can take a look to existing implementations in <a href="https://invenio-oauthclient.readthedocs.org">Invenio-OAuthClient</a>.</p> <p>In your <code>invenio.cfg</code>:</p> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">invenio_oauthclient.contrib.settings</span> <span class="kn">import</span> <span class="n">OauthSettingsHelper</span> <span class="k">class</span> <span class="nc">MyOAuthSettingsHelper</span><span class="p">(</span><span class="n">OAuthSettingsHelper</span><span class="p">):</span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span> <span class="n">title</span><span class="o">=</span><span class="s2">&quot;my plugin&quot;</span><span class="p">,</span> <span class="n">description</span><span class="o">=</span><span class="s2">&quot;a description&quot;</span><span class="p">,</span> <span class="n">base_url</span><span class="o">=</span><span class="s2">&quot;https://myserver.com/&quot;</span><span class="p">,</span> <span class="n">app_key</span><span class="o">=</span><span class="s2">&quot;MY_APP_CREDENTIALS&quot;</span><span class="p">,</span> <span class="n">access_token_url</span><span class="o">=</span><span class="s2">&quot;https://myserver.com/oauth/token&quot;</span><span class="p">,</span> <span class="n">authorize_url</span><span class="o">=</span><span class="s2">&quot;https://myserver.com/oauth/authorize&quot;</span><span class="p">,</span> <span class="p">)</span> <span class="k">def</span> <span class="nf">get_handlers</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="nb">dict</span><span class="p">(</span> <span class="n">authorized_handler</span><span class="o">=</span><span class="s1">&#39;invenio_oauthclient.handlers&#39;</span> <span class="s1">&#39;:authorized_signup_handler&#39;</span><span class="p">,</span> <span class="n">disconnect_handler</span><span class="o">=</span><span class="n">my_disconnect_handler</span><span class="p">,</span> <span class="n">signup_handler</span><span class="o">=</span><span class="nb">dict</span><span class="p">(</span> <span class="n">info</span><span class="o">=</span><span class="n">my_account_info</span><span class="p">,</span> <span class="n">setup</span><span class="o">=</span><span class="n">my_account_setup</span><span class="p">,</span> <span class="n">view</span><span class="o">=</span><span class="s1">&#39;invenio_oauthclient.handlers:signup_handler&#39;</span><span class="p">,</span> <span class="p">)</span> <span class="p">)</span> <span class="k">def</span> <span class="nf">get_rest_handlers</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="nb">dict</span><span class="p">(</span> <span class="n">authorized_handler</span><span class="o">=</span><span class="s1">&#39;invenio_oauthclient.handlers.rest&#39;</span> <span class="s1">&#39;:authorized_signup_handler&#39;</span><span class="p">,</span> <span class="n">disconnect_handler</span><span class="o">=</span><span class="n">my_disconnect_handler</span><span class="p">,</span> <span class="n">signup_handler</span><span class="o">=</span><span class="nb">dict</span><span class="p">(</span> <span class="n">info</span><span class="o">=</span><span class="n">my_account_info</span><span class="p">,</span> <span class="n">setup</span><span class="o">=</span><span class="n">my_account_setup</span><span class="p">,</span> <span class="n">view</span><span class="o">=</span><span class="s1">&#39;invenio_oauthclient.handlers.rest:signup_handler&#39;</span><span class="p">,</span> <span class="p">),</span> <span class="n">response_handler</span><span class="o">=</span><span class="s1">&#39;invenio_oauthclient.handlers.rest&#39;</span> <span class="s1">&#39;:default_remote_response_handler&#39;</span><span class="p">,</span> <span class="n">authorized_redirect_url</span><span class="o">=</span><span class="s1">&#39;/&#39;</span><span class="p">,</span> <span class="n">disconnect_redirect_url</span><span class="o">=</span><span class="s1">&#39;/&#39;</span><span class="p">,</span> <span class="n">signup_redirect_url</span><span class="o">=</span><span class="s1">&#39;/&#39;</span><span class="p">,</span> <span class="n">error_redirect_url</span><span class="o">=</span><span class="s1">&#39;/&#39;</span> <span class="p">)</span> <span class="p">)</span> <span class="k">def</span> <span class="nf">my_disconnect_handler</span><span class="p">(</span><span class="o">...</span><span class="p">):</span> <span class="o">...</span> <span class="k">def</span> <span class="nf">my_account_info</span><span class="p">(</span><span class="o">...</span><span class="p">):</span> <span class="o">...</span> <span class="k">def</span> <span class="nf">my_account_setup</span><span class="p">(</span><span class="o">...</span><span class="p">):</span> <span class="o">...</span> <span class="n">myOAuthHelper</span> <span class="o">=</span> <span class="n">MyOAuthSettingsHelper</span><span class="p">()</span> <span class="n">OAUTHCLIENT_REMOTE_APPS</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span> <span class="n">myoauth</span><span class="o">=</span><span class="n">myOAuthHelper</span><span class="o">.</span><span class="n">remote_app</span><span class="p">,</span> <span class="p">)</span> <span class="n">MY_APP_CREDENTIALS</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span> <span class="n">consumer_key</span><span class="o">=</span><span class="s2">&quot;&lt;my-key&gt;&quot;</span><span class="p">,</span> <span class="n">consumer_secret</span><span class="o">=</span><span class="s2">&quot;&lt;my-secret&gt;&quot;</span><span class="p">,</span> <span class="p">)</span> </code></pre></div> <h3 id="allowdeny-user-login">Allow/deny user login<a class="headerlink" href="#allowdeny-user-login" title="Permanent link">&para;</a></h3> <p>When using external authentication, you might want to allow or deny user login base on some conditions. For example, you might want to allow only users with e-mails from a specific domain <code>@mydomain.com</code>.</p> <p>To implement such functionality, you will have to change the implementation of the <code>signup_handler.info</code> handler. First, see how to implement a <a href="#new-oauth-plugins">new custom plugin</a>. You can also copy/paste an existing one.</p> <p>Then, implement the <code>signup_handler.info</code> handler as the following (example taken from Keycloak plugin):</p> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">flash</span> <span class="k">def</span> <span class="nf">account_info</span><span class="p">(</span><span class="n">remote</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span> <span class="n">user_info</span> <span class="o">=</span> <span class="n">get_user_info</span><span class="p">(</span><span class="n">remote</span><span class="p">,</span> <span class="n">resp</span><span class="p">)</span> <span class="n">email</span> <span class="o">=</span> <span class="n">user_info</span><span class="p">[</span><span class="s2">&quot;email&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">email</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="s2">&quot;@mydomain.com&quot;</span><span class="p">):</span> <span class="n">flash</span><span class="p">(</span><span class="n">_</span><span class="p">(</span><span class="s1">&#39;You are not allowed to login on this website.&#39;</span><span class="p">),</span> <span class="n">category</span><span class="o">=</span><span class="s1">&#39;danger&#39;</span><span class="p">)</span> <span class="n">abort</span><span class="p">(</span><span class="mi">401</span><span class="p">)</span> <span class="k">else</span><span class="p">:</span> <span class="c1"># default implementation</span> <span class="o">...</span> </code></pre></div> <h3 id="saml-integration">SAML integration<a class="headerlink" href="#saml-integration" title="Permanent link">&para;</a></h3> <div class="admonition warning"> <p class="admonition-title">Warning</p> <p>While the SAML integration has been tested and should be working following the guide below, it might still need some further updates, in particular to make its configuration easier.</p> </div> <p>SAML stands for Security Assertion Markup Language. It is an XML-based open-standard for transferring identity data between two parties: an identity provider (IdP) and a service provider (SP).</p> <ul> <li><strong>Identity Provider (IDP)</strong>: Performs authentication and passes the user's identity and authorization level to the service provider.</li> <li><strong>Service Provider (SP)</strong>: Trusts the identity provider and authorizes the given user to access the requested resource.</li> </ul> <h4 id="prerequisites">Prerequisites<a class="headerlink" href="#prerequisites" title="Permanent link">&para;</a></h4> <ul> <li>Make sure you have installed in your system:</li> </ul> <p><code>libxml2-dev libxmlsec1-dev pkg-config</code></p> <ul> <li> <p>Make sure you have installed the required Invenio Python module:</p> <div class="highlight"><pre><span></span><code><span class="go">cd my-site</span> <span class="go">pipenv run pip install invenio-saml</span> </code></pre></div> </li> </ul> <h4 id="server-information">Server information<a class="headerlink" href="#server-information" title="Permanent link">&para;</a></h4> <p>List of information required to configure the InvenioRDM instance.</p> <ul> <li> <p>SAML requires a x.509 cert to sign and encrypt elements like <code>NameID</code>, <code>Message</code>, <code>Assertion</code>, <code>Metadata</code>.</p> <ul> <li><strong>sp.crt</strong>: The public cert of the SP.</li> <li><strong>sp.key</strong>: The private key of the SP.</li> </ul> </li> <li> <p><strong>EntityID</strong>: Identifier of the IdP entity (must be a URI).</p> </li> <li><strong>SSO(singleSignOnService)</strong>: URL Target of the IdP where the Authentication Request Message will be sent.</li> <li><strong>SLO(singleLogoutService)</strong>: URL Location where the <LogoutRequest> from the IdP will be sent (IdP-initiated logout).</li> <li><strong>x509cert</strong>: Public X.509 certificate of the IdP.</li> <li> <p><strong>Attributes mapping</strong>: IDP in Assertion of the SAML Response provides a dict with all the user data:</p> <ul> <li>For example, given the following SAML response: <div class="highlight"><pre><span></span><code><span class="p">{</span> <span class="w"> </span><span class="nt">&quot;cn&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&quot;Jhon&quot;</span><span class="p">],</span> <span class="w"> </span><span class="nt">&quot;sn&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&quot;Doe&quot;</span><span class="p">],</span> <span class="w"> </span><span class="nt">&quot;mail&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&quot;john.doe@example.com&quot;</span><span class="p">],</span> <span class="w"> </span><span class="nt">&quot;external_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&quot;24546786764d&quot;</span><span class="p">]</span> <span class="p">}</span> </code></pre></div></li> <li>You can create a mapping to the user account fields required by Invenio as the following: <div class="highlight"><pre><span></span><code><span class="w"> </span><span class="nt">&quot;mappings&quot;</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="nt">&quot;email&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;mail&quot;</span><span class="p">,</span> <span class="w"> </span><span class="nt">&quot;name&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;cn&quot;</span><span class="p">,</span> <span class="w"> </span><span class="nt">&quot;surname&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;sn&quot;</span><span class="p">,</span> <span class="w"> </span><span class="nt">&quot;external_id&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;external_id&quot;</span><span class="p">,</span> <span class="w"> </span><span class="p">},</span> </code></pre></div></li> </ul> </li> </ul> <h4 id="configuration_1">Configuration<a class="headerlink" href="#configuration_1" title="Permanent link">&para;</a></h4> <p>In your <code>invenio.cfg</code>:</p> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">invenio_saml.handlers</span> <span class="kn">import</span> <span class="n">acs_handler_factory</span> <span class="n">SSO_SAML_DEFAULT_BLUEPRINT_PREFIX</span> <span class="o">=</span> <span class="s1">&#39;/saml&#39;</span> <span class="sd">&quot;&quot;&quot;Base URL for the extensions endpoint.&quot;&quot;&quot;</span> <span class="n">SSO_SAML_DEFAULT_METADATA_ROUTE</span> <span class="o">=</span> <span class="s1">&#39;/metadata/&lt;idp&gt;&#39;</span> <span class="sd">&quot;&quot;&quot;URL route for the metadata request.&quot;&quot;&quot;</span> <span class="n">SSO_SAML_DEFAULT_SSO_ROUTE</span> <span class="o">=</span> <span class="s1">&#39;/login/&lt;idp&gt;&#39;</span> <span class="sd">&quot;&quot;&quot;URL route for the SP login.&quot;&quot;&quot;</span> <span class="n">SSO_SAML_DEFAULT_ACS_ROUTE</span> <span class="o">=</span> <span class="s1">&#39;/authorized/&lt;idp&gt;&#39;</span> <span class="sd">&quot;&quot;&quot;URL route to handle the IdP login request.&quot;&quot;&quot;</span> <span class="n">SSO_SAML_DEFAULT_SLO_ROUTE</span> <span class="o">=</span> <span class="s1">&#39;/slo/&lt;idp&gt;&#39;</span> <span class="sd">&quot;&quot;&quot;URL route for the SP logout.&quot;&quot;&quot;</span> <span class="n">SSO_SAML_DEFAULT_SLS_ROUTE</span> <span class="o">=</span> <span class="s1">&#39;/sls/&lt;idp&gt;&#39;</span> <span class="sd">&quot;&quot;&quot;URL route to handle the IdP logout request.&quot;&quot;&quot;</span> <span class="n">SSO_SAML_IDPS</span> <span class="o">=</span> <span class="p">{</span> <span class="c1"># name your authentication provider</span> <span class="s1">&#39;remote_app&#39;</span><span class="p">:</span> <span class="p">{</span> <span class="c1"># Basic info</span> <span class="s2">&quot;title&quot;</span><span class="p">:</span> <span class="s2">&quot;SAML&quot;</span><span class="p">,</span> <span class="s2">&quot;description&quot;</span><span class="p">:</span> <span class="s2">&quot;SAML Authentication Service&quot;</span><span class="p">,</span> <span class="s2">&quot;icon&quot;</span><span class="p">:</span> <span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="c1"># path to the file i.e. &quot;./saml/sp.crt&quot;</span> <span class="s1">&#39;sp_cert_file&#39;</span><span class="p">:</span> <span class="s1">&#39;&lt;./SP_CERT_FILE&gt;&#39;</span><span class="p">,</span> <span class="c1"># path to the file i.e. &quot;./saml/sp.key&quot;</span> <span class="s1">&#39;sp_key_file&#39;</span><span class="p">:</span> <span class="s1">&#39;&lt;./SP_KEY_FILE&gt;&#39;</span><span class="p">,</span> <span class="s1">&#39;settings&#39;</span><span class="p">:</span> <span class="p">{</span> <span class="c1"># If strict is True, then the Python Toolkit will reject unsigned</span> <span class="c1"># or unencrypted messages if it expects them to be signed or encrypted.</span> <span class="c1"># Also it will reject the messages if the SAML standard is not strictly</span> <span class="c1"># followed. Destination, NameId, Conditions ... are validated too.</span> <span class="s1">&#39;strict&#39;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span> <span class="c1"># Enable debug mode (outputs errors).</span> <span class="s1">&#39;debug&#39;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span> <span class="c1"># Service Provider Data that we are deploying.</span> <span class="s1">&#39;sp&#39;</span><span class="p">:</span> <span class="p">{</span> <span class="c1"># Specifies the constraints on the name identifier to be used to</span> <span class="c1"># represent the requested subject.</span> <span class="c1"># Take a look on https://github.com/onelogin/python-saml/blob/master/src/onelogin/saml2/constants.py</span> <span class="c1"># to see the NameIdFormat that are supported.</span> <span class="s1">&#39;NameIDFormat&#39;</span><span class="p">:</span> <span class="s1">&#39;urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified&#39;</span><span class="p">,</span> <span class="p">},</span> <span class="c1"># Identity Provider Data that we want connected with our SP.</span> <span class="s1">&#39;idp&#39;</span><span class="p">:</span> <span class="p">{</span> <span class="c1"># Identifier of the IdP entity (must be a URI)</span> <span class="s1">&#39;entityId&#39;</span><span class="p">:</span> <span class="s1">&#39;&lt;IdP_Entity&gt;&#39;</span><span class="p">,</span> <span class="c1"># SSO endpoint info of the IdP. (Authentication Request protocol)</span> <span class="s1">&#39;singleSignOnService&#39;</span><span class="p">:</span> <span class="p">{</span> <span class="c1"># URL Target of the IdP where the Authentication Request Message</span> <span class="c1"># will be sent.</span> <span class="s1">&#39;url&#39;</span><span class="p">:</span> <span class="s1">&#39;&lt;SSO_URL&gt;&#39;</span><span class="p">,</span> <span class="c1"># SAML protocol binding to be used when returning the &lt;Response&gt;</span> <span class="c1"># message. OneLogin Toolkit supports the HTTP-Redirect binding</span> <span class="c1"># only for this endpoint.</span> <span class="s1">&#39;binding&#39;</span><span class="p">:</span> <span class="s1">&#39;urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect&#39;</span> <span class="p">},</span> <span class="c1"># SLO endpoint info of the IdP.</span> <span class="s1">&#39;singleLogoutService&#39;</span><span class="p">:</span> <span class="p">{</span> <span class="c1"># URL Location where the &lt;LogoutRequest&gt; from the IdP will be sent (IdP-initiated logout)</span> <span class="s1">&#39;url&#39;</span><span class="p">:</span> <span class="s1">&#39;&lt;SLS_URL&gt;&#39;</span><span class="p">,</span> <span class="c1"># SAML protocol binding to be used when returning the &lt;Response&gt;</span> <span class="c1"># message. OneLogin Toolkit supports the HTTP-Redirect binding</span> <span class="c1"># only for this endpoint.</span> <span class="s1">&#39;binding&#39;</span><span class="p">:</span> <span class="s1">&#39;urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect&#39;</span> <span class="p">},</span> <span class="c1"># Public X.509 certificate of the IdP</span> <span class="s1">&#39;x509cert&#39;</span><span class="p">:</span> <span class="s1">&#39;&lt;X.509_oneliner&gt;&#39;</span> <span class="p">},</span> <span class="c1"># Security settings</span> <span class="c1"># more on https://github.com/onelogin/python-saml</span> <span class="s1">&#39;security&#39;</span><span class="p">:</span> <span class="p">{</span> <span class="s1">&#39;authnRequestsSigned&#39;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span> <span class="s1">&#39;failOnAuthnContextMismatch&#39;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span> <span class="s1">&#39;logoutRequestSigned&#39;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span> <span class="s1">&#39;logoutResponseSigned&#39;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span> <span class="s1">&#39;metadataCacheDuration&#39;</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span> <span class="s1">&#39;metadataValidUntil&#39;</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span> <span class="s1">&#39;nameIdEncrypted&#39;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span> <span class="s1">&#39;requestedAuthnContext&#39;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span> <span class="s1">&#39;requestedAuthnContextComparison&#39;</span><span class="p">:</span> <span class="s1">&#39;exact&#39;</span><span class="p">,</span> <span class="s1">&#39;signMetadata&#39;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span> <span class="s1">&#39;signatureAlgorithm&#39;</span><span class="p">:</span> <span class="s1">&#39;http://www.w3.org/2001/04/xmldsig-more#rsa-sha256&#39;</span><span class="p">,</span> <span class="s1">&#39;wantAssertionsEncrypted&#39;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span> <span class="s1">&#39;wantAssertionsSigned&#39;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span> <span class="s1">&#39;wantAttributeStatement&#39;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span> <span class="s1">&#39;wantMessagesSigned&#39;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span> <span class="s1">&#39;wantNameId&#39;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span> <span class="s1">&#39;wantNameIdEncrypted&#39;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span> <span class="s1">&#39;digestAlgorithm&#39;</span><span class="p">:</span> <span class="s1">&#39;http://www.w3.org/2001/04/xmlenc#sha256&#39;</span> <span class="p">},</span> <span class="p">},</span> <span class="c1"># Account Mapping</span> <span class="s2">&quot;mappings&quot;</span><span class="p">:</span> <span class="p">{</span> <span class="s2">&quot;email&quot;</span><span class="p">:</span> <span class="s2">&quot;&lt;attribute_email&gt;&quot;</span><span class="p">,</span> <span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;&lt;attribute_name&gt;&quot;</span><span class="p">,</span> <span class="s2">&quot;surname&quot;</span><span class="p">:</span> <span class="s2">&quot;&lt;attribute_surname&gt;&quot;</span><span class="p">,</span> <span class="s2">&quot;external_id&quot;</span><span class="p">:</span> <span class="s2">&quot;&lt;attribute_external_id&gt;&quot;</span><span class="p">,</span> <span class="p">},</span> <span class="c1"># Inject your remote_app to handler</span> <span class="c1"># Note: keep in mind the string should match</span> <span class="c1"># given name for authentication provider</span> <span class="s1">&#39;acs_handler&#39;</span><span class="p">:</span> <span class="n">acs_handler_factory</span><span class="p">(</span><span class="s1">&#39;remote_app&#39;</span><span class="p">),</span> <span class="c1"># Automatically set `confirmed_at` for users upon</span> <span class="c1"># registration, when using the default `acs_handler`</span> <span class="s1">&#39;auto_confirm&#39;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div> <h4 id="automatically-confirm-users">Automatically confirm users<a class="headerlink" href="#automatically-confirm-users" title="Permanent link">&para;</a></h4> <p>When using the default <code>acs_handler</code> for an authentication provider, users can be automatically confirmed upon registration by setting the <code>auto_confirm</code> attribute of a provider to <code>True</code> (default is <code>False</code>). This will set the <code>confirmed_at</code> attribute of the user to the current time.</p> <p>This can be done on a per provider basis, as not every provider may receive the same level of trust for a repository.</p> <p>The <code>mappings</code> attributes are critical to getting your SAML configuration working correctly. The values for <code>&lt;attibute_email&gt;</code>, <code>&lt;attribute_name&gt;</code>, <code>&lt;attribute_surname&gt;</code> and <code>&lt;attribute_external_id&gt;</code> are set in the <code>urn:oid</code> notation. If you are coming from an institution that uses the eduPerson as what is returned from your IdP you'll need to make those names into the <code>urn:oid</code> form. Example mapping of <code>urn:oasis</code> to <code>urn:oid</code> like those provided at <a href="https://spaces.ais.ucla.edu/display/iamucladocs/Mapping+of+URN+Attributes+and+OID+Attributes">University of California</a> administrative information systems website may provide a clue to how your institution needs to map the SAML response from the IdP.</p> <p>NOTE: In the above example the <code>&lt;idp&gt;</code> values do not need to be replaced. Invenio-RDM will map those internally.</p> <h5 id="example-configurations-element">Example configurations element<a class="headerlink" href="#example-configurations-element" title="Permanent link">&para;</a></h5> <p>Here's an example mapping eduPerson elements to <code>urn:oid</code>. If you're not sure of your institutions mappings reach out to your IdP and search for "<code>eduPerson to</code>urn:oid`" and see what others have documented.</p> <div class="highlight"><pre><span></span><code> &quot;mappings&quot;: { # email mapped form eduPerson.mail &quot;email&quot;: &quot;urn:oid:0.9.2342.19200300.100.1.3&quot;, # name mapped from eduPerson.givenName &quot;name&quot;: &quot;urn:oid:2.5.4.42&quot;, # surname maps to eduPerson.sn &quot;surname&quot;: &quot;urn:oid:2.5.4.4&quot;, # external_id mapps to eduPerson.eduPersonPrincipalName # (e.g. jane.doe@example.edu) &quot;external_id&quot;: &quot;urn:oid:1.3.6.1.4.1.5923.1.1.1.6&quot;, }, </code></pre></div> <h5 id="troubleshooting-saml-configuration">Troubleshooting SAML configuration<a class="headerlink" href="#troubleshooting-saml-configuration" title="Permanent link">&para;</a></h5> <p>In setting up SAML integration you may run into several scenarios before you "get it right".</p> <p>What are the InvenioRDM SP end points? : In the example above the <code>SSO_SAML_IDPS</code> is a dictionary, the attributes are the names that will be used by InvenioRDM in the SAML interactions. In the example "remote_app" will be an end point, this is probably not ideal, if you have one IdP only you could just name that attribute "ipd", if you have several then a more descriptive attribute name might be warranted. </p> <p>SSO redirects work but Invenio shows 404 on return : This can happen when the IdP is configured in InvenioRDM, the IdP has authorized your SP (the running InvenioRDM instance). If the user isn't actually "logged in" then you may also have trouble in your <code>mappings</code> element.</p> <p>SSO redirects work, InvenioRDM returns a dashboard : It is possible to have SAML/Shibboleth work from some users and not others. This maybe cause by an incorrect <code>mappings</code>. Double check that the values needed by InvenioRDM are getting correct responses, this can be done from checking your system logs for the running InvenioRDM instance.</p> <p>SSO logout fails : This could be as simple as correcting the <code>url</code> value in the <code>Idp</code>, <code>singleLogoutService</code> section. If ancient versions of SAML/Shibboleth did not support "logging out".</p> <h4 id="show-the-login-button">Show the login button<a class="headerlink" href="#show-the-login-button" title="Permanent link">&para;</a></h4> <p>The last step is to enable the login template, provided by the SAML module, to display the new button <code>Login with SAML</code>. In your <code>invenio.cfg</code>:</p> <div class="highlight"><pre><span></span><code><span class="n">OAUTHCLIENT_LOGIN_USER_TEMPLATE</span> <span class="o">=</span> <span class="s2">&quot;invenio_saml/login_user.html&quot;</span> </code></pre></div> <h4 id="multiple-saml-authentication-providers">Multiple SAML authentication providers<a class="headerlink" href="#multiple-saml-authentication-providers" title="Permanent link">&para;</a></h4> <p>You might have the need to integrate multiple SAML authentication providers at the same time, to allow users to log in with one or the other. You can define multiple SAML apps in your <code>invenio.cfg</code>:</p> <div class="highlight"><pre><span></span><code><span class="n">SSO_SAML_IDPS</span> <span class="o">=</span> <span class="p">{</span> <span class="c1"># First authentication provider</span> <span class="s2">&quot;remote_app&quot;</span><span class="p">:</span> <span class="p">{</span> <span class="o">....</span> <span class="s1">&#39;settings&#39;</span><span class="p">:</span> <span class="p">{</span> <span class="o">....</span> <span class="p">},</span> <span class="o">....</span> <span class="s1">&#39;acs_handler&#39;</span><span class="p">:</span> <span class="n">acs_handler_factory</span><span class="p">(</span><span class="s1">&#39;remote_app&#39;</span><span class="p">),</span> <span class="s1">&#39;auto_confirm&#39;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span> <span class="p">},</span> <span class="c1"># Second authentication provider</span> <span class="s2">&quot;remote_app2&quot;</span><span class="p">:</span> <span class="p">{</span> <span class="o">....</span> <span class="s1">&#39;settings&#39;</span><span class="p">:</span> <span class="p">{</span> <span class="o">....</span> <span class="p">},</span> <span class="o">....</span> <span class="s1">&#39;acs_handler&#39;</span><span class="p">:</span> <span class="n">acs_handler_factory</span><span class="p">(</span><span class="s1">&#39;remote_app2&#39;</span><span class="p">),</span> <span class="s1">&#39;auto_confirm&#39;</span><span class="p">:</span> <span class="kc">False</span><span class="p">,</span> <span class="p">},</span> <span class="p">}</span> </code></pre></div> <h3 id="groups">Groups<a class="headerlink" href="#groups" title="Permanent link">&para;</a></h3> <p>A <code>group</code> is a set of users that can be managed in your organization, externally to InvenioRDM.</p> <p>Groups can be useful, for example, to externally manage access and roles of communities' members, without hard coding the list of users in your InvenioRDM instance. Another possible scenario, not yet supported, could be to grant or restrict access to other resources, such as records or files.</p> <p>The support of groups is a feature introduced in the release <a href="https://inveniordm.docs.cern.ch/releases/versions/version-v9.0.0/">v9.0</a>.</p> <p>When integrating groups in your InvenioRDM instance, you will have to:</p> <ol> <li>Import and keep in sync a copy of the groups available in your organization in the InvenioRDM database. This is needed to be able to search for groups when granting/restricting access to resources.</li> <li>When the user signs in, "assign" to the user the list of groups to which (s)he belongs. Read more below.</li> </ol> <div class="admonition notice"> <p class="admonition-title">Notice</p> <p>How to import and keep in sync the local copy of groups with your organization' groups is outside the scope of the InvenioRDM documentation and highly depends on your organization's policies, constraints and technologies.</p> </div> <h4 id="add-groups">Add groups<a class="headerlink" href="#add-groups" title="Permanent link">&para;</a></h4> <p>In InvenioRDM, groups are simply treated as <code>Roles</code>. To add a group, you create a role:</p> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">invenio_accounts.proxies</span> <span class="kn">import</span> <span class="n">current_datastore</span> <span class="n">current_datastore</span><span class="o">.</span><span class="n">create_role</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s2">&quot;it-dep&quot;</span><span class="p">,</span> <span class="n">description</span><span class="o">=</span><span class="s2">&quot;The group containing all users of the IT department.&quot;</span><span class="p">)</span> <span class="n">current_datastore</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span> </code></pre></div> <h4 id="assign-groups-on-login">Assign groups on login<a class="headerlink" href="#assign-groups-on-login" title="Permanent link">&para;</a></h4> <p>When the user signs in, you will have to add the user's groups as <code>needs</code> (technically <code>RoleNeed</code>). If an intersection between the <code>RoleNeed</code> (the groups) that the user provides and the <code>RoleNeed</code> that the resource requires exists, then the user has access.</p> <p>With the example above, a user providing a <code>RoleNeed('it-dep')</code> will have access to a resource requiring the <code>RoleNeed('it-dep')</code>.</p> <p>Below, you can find <strong>an example</strong> of how you can add a <code>RoleNeed</code> on login.</p> <div class="admonition warning"> <p class="admonition-title">Use at your own risk</p> <p>The integration of groups is not fully tested yet and the code below is just an example of a possible implementation.</p> </div> <p>Assuming you're implementing a custom <a href="https://inveniordm.docs.cern.ch/customize/authentication/#new-oauth-plugins">OAuth plugin</a>, the fetching of user groups can happen after having fetched user information with the <a href="https://inveniordm.docs.cern.ch/customize/authentication/#allowdeny-user-login"><code>signup_handler.info</code></a> handler.</p> <div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">info_handler</span><span class="p">(</span><span class="n">remote</span><span class="p">,</span> <span class="n">resp</span><span class="p">):</span> <span class="o">...</span> <span class="c1"># existing code</span> <span class="n">user_info</span> <span class="o">=</span> <span class="n">get_user_info</span><span class="p">(</span><span class="n">remote</span><span class="p">,</span> <span class="n">resp</span><span class="p">)</span> <span class="o">...</span> <span class="c1"># your implementation: fetch groups synchronously</span> <span class="n">roles_or_groups_names</span> <span class="o">=</span> <span class="n">fetch_roles_or_groups_names</span><span class="p">(</span><span class="n">remote</span><span class="p">,</span> <span class="n">user_info</span><span class="p">)</span> <span class="n">provides</span> <span class="o">=</span> <span class="nb">set</span><span class="p">(</span><span class="n">UserNeed</span><span class="p">(</span><span class="n">current_user</span><span class="o">.</span><span class="n">email</span><span class="p">))</span> <span class="c1"># add groups as Invenio roles to user session</span> <span class="k">for</span> <span class="n">name</span> <span class="ow">in</span> <span class="n">roles_or_groups_names</span><span class="p">:</span> <span class="n">provides</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">RoleNeed</span><span class="p">(</span><span class="n">name</span><span class="p">))</span> <span class="n">identity</span><span class="o">.</span><span class="n">provides</span> <span class="o">|=</span> <span class="n">provides</span> <span class="n">session</span><span class="p">[</span><span class="s2">&quot;&lt;my_external_app_name&gt;_roles&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="n">provides</span> <span class="c1"># end your implementation</span> <span class="o">...</span> </code></pre></div> <p>The <code>fetch_roles_or_groups_names</code> might retrieve the user groups from the <code>user_info</code> attributes previously fetched or perform a new network request to fetch them from other REST APIs.</p> <p>When groups cannot be retrieved synchronously in the same HTTP request (slow or heavy task), a possible solution could be:</p> <ol> <li>Fetch user groups async in a celery task and store it in the database. The <code>RemoteAccount</code> database table contains a <code>extra_data</code> column which could be used to "cache" user groups.</li> <li>On login, enrich the user session identity <code>provides</code> by reading the list of groups from the database.</li> <li>Refresh the list of user groups of each user when it makes sense in the organization context.</li> </ol> </article> </div> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> </main> <footer class="md-footer"> <nav class="md-footer__inner md-grid" aria-label="Footer" > <a href="../look-and-feel/font/" class="md-footer__link md-footer__link--prev" aria-label="Previous: Change font"> <div class="md-footer__button md-icon"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg> </div> <div class="md-footer__title"> <span class="md-footer__direction"> Previous </span> <div class="md-ellipsis"> Change font </div> </div> </a> <a href="../emails/" class="md-footer__link md-footer__link--next" aria-label="Next: Sending emails"> <div class="md-footer__title"> <span class="md-footer__direction"> Next </span> <div class="md-ellipsis"> Sending emails </div> </div> <div class="md-footer__button md-icon"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11z"/></svg> </div> </a> </nav> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class="md-copyright"> <div class="md-copyright__highlight"> Copyright &copy; 2019-2024 CERN, Northwestern University and contributors. </div> Made with <a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener"> Material for MkDocs </a> </div> <div class="md-social"> <a href="https://github.com/inveniosoftware" target="_blank" rel="noopener" title="github.com" class="md-social__link"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><!--! Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc.--><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8M97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg> </a> </div> </div> </div> </footer> </div> <div class="md-dialog" data-md-component="dialog"> <div class="md-dialog__inner md-typeset"></div> </div> <script id="__config" type="application/json">{"base": "../..", "features": ["navigation.tabs", "navigation.footer", "content.code.copy"], "search": "../../assets/javascripts/workers/search.6ce7567c.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}}</script> <script src="../../assets/javascripts/bundle.525ec568.min.js"></script> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10