<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Auth | 2i2c</title><link>https://deploy-preview-631--2i2c-org.netlify.app/tag/auth/</link><atom:link href="https://deploy-preview-631--2i2c-org.netlify.app/tag/auth/index.xml" rel="self" type="application/rss+xml"/><description>Auth</description><generator>Hugo Blox Builder (https://hugoblox.com)</generator><language>en-us</language><lastBuildDate>Wed, 26 Aug 2026 00:00:00 +0000</lastBuildDate><image><url>https://deploy-preview-631--2i2c-org.netlify.app/media/sharing.png</url><title>Auth</title><link>https://deploy-preview-631--2i2c-org.netlify.app/tag/auth/</link></image><item><title>Building an authentication helper library for JupyterHub</title><link>https://deploy-preview-631--2i2c-org.netlify.app/blog/buliding-jhub-authz/</link><pubDate>Wed, 26 Aug 2026 00:00:00 +0000</pubDate><guid>https://deploy-preview-631--2i2c-org.netlify.app/blog/buliding-jhub-authz/</guid><description>&lt;p>2i2c have been working with the &lt;a href="https://deploy-preview-631--2i2c-org.netlify.app/collaborators/utoronto/" >University of Toronto&lt;/a> to add support for logging in to a JupyterHub using Canvas as an Identity Provider. JupyterHub implements support for &lt;em>authenticating&lt;/em> users (figuring out who there are) and &lt;em>authorizing&lt;/em> users (figuring out whether they are permitted to log into the hub) using an &lt;code>Authenicator&lt;/code> interface. Via the &lt;code>oauthenticator&lt;/code> package, JupyterHub has support for many kinds of OAuth2-based Identity Providers such as Google or GitHub, in addition to providing a &lt;em>generic&lt;/em> authenticator &lt;code>GenericOAuthenticator&lt;/code> that can be used with any OAuth2 implementation.&lt;/p>
&lt;figure id="figure-diagram-of-an-oauth-flow-during-the-authentication-process-taken-from-the-oauthenticator-documentationhttpsoauthenticatorreadthedocsioenlatest_imagesjupyterhub-oauth-external-flowpng">
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="Diagram of an OAuth flow during the authentication process, taken from [the oauthenticator documentation](https://oauthenticator.readthedocs.io/en/latest/_images/JupyterHub-OAuth-external-flow.png)." srcset="
/blog/buliding-jhub-authz/oauth-flow_hu0c8a8127bb8c611237d98151170d7a50_76835_3b90dd686be44dc5e5271fe8b6a74d81.webp 400w,
/blog/buliding-jhub-authz/oauth-flow_hu0c8a8127bb8c611237d98151170d7a50_76835_b76b2c9bead95c9862c4df0ce3110d69.webp 760w,
/blog/buliding-jhub-authz/oauth-flow_hu0c8a8127bb8c611237d98151170d7a50_76835_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://deploy-preview-631--2i2c-org.netlify.app/blog/buliding-jhub-authz/oauth-flow_hu0c8a8127bb8c611237d98151170d7a50_76835_3b90dd686be44dc5e5271fe8b6a74d81.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;figcaption>
Diagram of an OAuth flow during the authentication process, taken from &lt;a href="https://oauthenticator.readthedocs.io/en/latest/_images/JupyterHub-OAuth-external-flow.png" target="_blank" rel="noopener" >the oauthenticator documentation&lt;/a>.
&lt;/figcaption>&lt;/figure>
&lt;p>Our friends at UC Berkeley maintain the &lt;code>CanvasOAuthenticator&lt;/code> project, an implementation of a Canvas OAuth2 authenticator for JupyterHub. This project has been in-use by UC Berkeley for over four years, authenticating thousands of students. In the time since &lt;code>CanvasOAuthenticator&lt;/code> was first written (with involvement from 2i2c members), the &lt;code>GenericOAuthenticator&lt;/code> has seen significant improvements, such that it is now possible to implement Canvas authentication without maintaining an entirely dedicated authenticator project.&lt;/p>
&lt;p>After recognizing that it is much easier to maintain a small set of OAuth2 rules (adaptors) for each Identity Provider (like Canvas) rather than full-blown authenticators, 2i2c built &lt;a href="https://2i2c.org/jupyterhub-oauthenticator-authz-helpers/" target="_blank" rel="noopener" >&lt;code>jupyterhub_oauthenticator_authz_helpers&lt;/code>&lt;/a>. This library maintains an implementation of a subset of UC Berkeley&amp;rsquo;s &lt;code>CanvasOAuthenticator&lt;/code> rules for mapping Canvas courses and groups onto JupyterHub groups. With this work, adding Canvas authentication requires only a few additional lines:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">jupyterhub_oauthenticator_authz_helpers.canvas&lt;/span> &lt;span class="n">build_auth_urls&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cfg&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">GenericOAuthenticator&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">canvas_url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;lt;CANVAS-URL&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Configure auths&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cfg&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">authorize_url&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cfg&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">token_url&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cfg&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">userdata_url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">build_auth_urls&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">canvas_url&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Scopes that this token will need&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cfg&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">scope&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">build_auth_urls&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">scopes&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Adding support for group population (from Canvas groups and courses) requires a little bit more config:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">jupyterhub_oauthenticator_authz_helpers.canvas&lt;/span> &lt;span class="n">get_user_groups&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">get_course_groups&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">build_auth_urls&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Define a custom key for auth groups&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cfg&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">auth_state_groups_key&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;custom-groups&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Define and inject auth state&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">auth_state_hook&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">authenticator&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">auth_state&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">auth_state&lt;/span> &lt;span class="ow">is&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">None&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">access_token&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">auth_state&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;access_token&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">auth_state&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">authenticator&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">auth_state_groups_key&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Populate groups from Canvas courses, using the scheme defined in get_course_groups&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">*&lt;/span>&lt;span class="k">await&lt;/span> &lt;span class="n">get_course_groups&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">canvas_url&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">access_token&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;course_code&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Populate groups from Canvas groups, using the scheme defined in get_user_groups&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">*&lt;/span>&lt;span class="k">await&lt;/span> &lt;span class="n">get_user_groups&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">canvas_url&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">access_token&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">auth_state&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cfg&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">modify_auth_state_hook&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">auth_state_hook&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Request additional scopes for the course / user group fetching&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cfg&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">scope&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">build_auth_urls&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">scopes&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">get_user_groups&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">scopes&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">get_course_groups&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">scopes&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In addition to building out this library, 2i2c hopes to foster collaboration with other users and maintainers to continue to upstream additional OAuth2 adaptors. As part of the funded initiative, 2i2c will spend time conducting gonvernance and community work upstream to establish a &lt;a href="https://github.com/jupyterhub-contrib" target="_blank" rel="noopener" >jupyterhub-contrib&lt;/a> organisation that provides stewardship and maintainance of this work, and future third-party projects.&lt;/p>
&lt;h2 id="acknowledgements">
Acknowledgements
&lt;a class="header-anchor" href="#acknowledgements">#&lt;/a>
&lt;/h2>&lt;ul>
&lt;li>Thanks to the &lt;a href="https://deploy-preview-631--2i2c-org.netlify.app/collaborators/utoronto/" >University of Toronto&lt;/a> for collaborating with us, and funding this work.&lt;/li>
&lt;li>Thanks to UC Berkeley for the prior work that inspired this project.&lt;/li>
&lt;/ul></description></item></channel></rss>