flask-security icon indicating copy to clipboard operation
flask-security copied to clipboard

TLS client-cert authentication built-in?

Open TaaviE opened this issue 5 years ago • 10 comments

Given that client certificates are commonly regarded as the best method of verifying if the user is authorized or not it would be really nice if Flask-Security could allow such logins with less setup required.

It's also worth mentioning that client certificates are something that quite a lot of countries provide to their citizens (for ex. Estonia, Latvia, Belgium, Finland) and is very commonly used in at least Estonia so there'd definitely be a relatively large user-base for such integration.

I myself implemented it by reusing Flask-Dance's OAuth link schema and providing a few endpoints to accept client-certificates. It would be much easier if common web-server configuration examples and etc. were provided and the endpoints with necessary checks existed.

TaaviE avatar Aug 09 '19 18:08 TaaviE

Intriguing - can you point to some code or documentation on exactly how this would work?

jwag956 avatar Aug 09 '19 22:08 jwag956

TLS client certificate as such is specified by the TLS spec. Individual web servers, such as nginx and Apache have their own configuration options for requesting the certificates from the client based on some chain of trust.

After a web server such as nginx has requested, received and verified the certificate it got one basically has the entire client certificate .pem if needed. After that it's up to the implementation how user lookup and auth is done.

I implemented it by just taking $ssl_client_verify, made sure the certificate is issued by the right issuer. After that I took $ssl_client_s_dn's hash, looked that up from the database and then logged the person in if it matched someone. Downside to that approach is that a new cert issued to the same person could mean they get a different DN. Doing it the "proper way", would be parsing out personal identification numbers from all the different countries' DN fields (Estonia, Latvia, etc.) and looking up the user based on that, I didn't do that because I didn't have the resources.

Due to web server limitations I also had to implement a redirect from main domain to the TLS client cert domain, do the authentication (set the cookie) and redirect back, Flask-Security could simplify this quite a bit I think.

https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_client_certificate https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_trusted_certificate https://nginx.org/en/docs/http/ngx_http_ssl_module.html#var_ssl_client_verify

TaaviE avatar Aug 14 '19 13:08 TaaviE

Thanks for the details. So you get the DN from whatever is doing ssl termination. Then you need to either look that up or map it first (using some kind of custom code) then look it up. Seems like that is quite doable today - by creating an @cert_required decorator - then picking the proper request (uwsgi) parameter, and doing any required mapping then issuing a find_user(dn=xxx).

I am not quite seeing what FS could bring since where to find the DN in the parameters would need to be configured, and any sort of mapping from DN to ID would have to be a plugin.

I suppose one nice thing would be that you could annotate @auth_required("token", "cert") which would be convenient. Seems pretty simple really. But I wouldn't try to include mapping algorithms in FS - just allow a callback.

jwag956 avatar Aug 14 '19 19:08 jwag956

I am not quite seeing what FS could bring

I'm thinking that FS could provide:

  • Documented procedure on how to provide cert/eID auth.
  • A configuration option (ENABLE_EID_AUTH?) that would seamlessly provide the endpoint to verify DN (just like it provides an endpoint to verify passwords) and set the cookie for the right domain (if nginx is used, how to do it nicely for everyone I don't know yet).
  • I also think having a field-tested universal code for parsing different countries' eID DNs would be something that requires bigger collaboration so having that would be nice as well.

TaaviE avatar Aug 15 '19 08:08 TaaviE

Ok - so it would be more like a new view: /login-cert that would grab uwsgi/request environment variables, invoke a DN to 'id' and look that up, and if everything ok - would login the user just as username/password does today. From their they can use session or token authentication for future requests...

Not sure your concern with cookie - I believe FS / Flask manages session cookie domains correctly (i.e. it has various config parameters to manage it).

DO you have any pointers to these eID/ country certs? This sure feels like a separate (small) package.

jwag956 avatar Aug 15 '19 17:08 jwag956

Not sure your concern with cookie - I believe FS / Flask manages session cookie domains correctly (i.e. it has various config parameters to manage it).

Yes, but it doesn't by-default set a cookie for an another domain name other than the one it's hosted from, with cert auth it needs to set the cookie for one level higher (app.domain.com instead of certauth.app.domain.com for example), that could be automatic.

DO you have any pointers to these eID/ country certs?

https://eid.eesti.ee/index.php/Authenticating_in_web_applications http://wiki.yobi.be/wiki/Belgian_eID

TaaviE avatar Aug 15 '19 17:08 TaaviE

Thanks for the links - interesting. Can you explain the cookie issue? Why would you need a subdomain called certauth.app.domain.com?

jwag956 avatar Aug 15 '19 21:08 jwag956

Why would you need a subdomain called certauth.app.domain.com?

Because for example nginx can't ask a client-cert in a path block, only on a server block. Just as a clarifying example /certauth (that requests the cert from the client) can't be done, certauth.app.domain.com however can be done.

TaaviE avatar Aug 16 '19 08:08 TaaviE

Thanks for the clarification. Can you point me to some examples of sites that actually accept this? Do they start at app.domain.com - then redirect if you want to log in using cert? how does that actually work.

As for mapping DN to ID - I do think that should be a separate package - mostly so that it can release/evolve separately from FS. We can make it seamless - if that package is installed - FS automatically picks it up.

jwag956 avatar Aug 21 '19 14:08 jwag956

Can you point me to some examples of sites that actually accept this?

https://tara.ria.ee/login https://my.zone.eu/et/zid/server/auth https://id.seb.ee/cgi-bin/ipank/ipank.p https://maasikas.emta.ee

Using cert auth is called ID-kaart in Estonian, Log in is usually Logi sisse or Sisene

As for mapping DN to ID - I do think that should be a separate package - mostly so that it can release/evolve separately from FS. We can make it seamless - if that package is installed - FS automatically picks it up.

It's not that much code though? I suspect it could maybe be done with a few regexes but I lack other ID-cards.

TaaviE avatar Aug 21 '19 15:08 TaaviE

With passkeys/webauthn gaining traction - I can't see adding this.

jwag956 avatar Nov 09 '23 01:11 jwag956