Hacker News new | ask | show | jobs
Show HN: Keratin AuthN – Accounts and Auth Microservice in Go (keratin.tech)
104 points by cainlevy 3142 days ago
13 comments

We're currently using this as our auth layer for https://empatico.org, so it's production ready!
How does it compare to Hydra/Dex? What I'm missing is a page that tells me where it sits in the ecosystem. A versus page if you will.
I'd love to fill that in! If anyone would like a comparison, please add links in this thread and I'll reply. Later, I can collect it into a published page.
That would be great, thanks! Here are the links to the projects I mentioned.

https://github.com/coreos/dex https://github.com/ory/hydra

> Dex is NOT a user-management system, but acts as a portal to other identity providers through "connectors."

> ORY Hydra is not an identity provider (user sign up, user log in, password reset flow), but connects to your existing identity provider through a consent app.

AuthN IS all the things that Dex and Hydra say they are not. I'll bet it could integrate with both given a bit of investment, e.g. by satisfying the "consent app" expectations.

AuthN does use as much of the OpenID Connect protocol as I could manage though. I started there and streamlined down to optimize for API-driven interactions rather than the redirect-driven interactions that are common with OAuth and OIC.

https://auth0.com

I've just started dabbling on a small project and would be interested to understand how features overlap and differences in license/distribution model.

Auth0 is top-notch SaaS. I have only good things to say about their product.

Aside from being OSS, one major difference is that Keratin AuthN is purely an API. It's optimized for customization so that it will fit with any bespoke (secure) UX you want to provide. I found Auth0's API to be something of an after-thought, second to their hosted/branded/templatable pages.

From the Dex README:

"Dex is NOT a user-management system, but acts as a portal to other identity providers through "connectors." This lets dex defer authentication to LDAP servers, SAML providers, or established identity providers like GitHub, Google, and Active Directory."

It seems like AuthN IS a user management system. So that's a big difference right there.

Hydra and Dex both support OAuth and OpenID Connect. This apparently supports neither, but comes with its own JWT structure.

With inbound federation that shouldn't be much of a problem, but with outbound federation you'll have some very difficult questions to answer (especially because all major identity solutions are pretty much OIC centric these days)

Yeah, I don't expect this JWT scheme to become an adopted standard. It's been streamlined from OIC for the narrow use case of working tightly with a trusted app.

Adding support for inbound federation is on the roadmap. Support for outbound federation using OIC isn't out of the question either, but I don't yet see the motivation.

Today I was evaluating what I we should use for something like this, a unified Facade with an API.

We evaluated Traefik and Kong. Decision was for Kong, since we need more features like auth, logging, rate limit.

Strong choice! My dream is for AuthN to provide authentication and account functionality for folks who have not yet invested in an API gateway, and then seamlessly plug in when their architecture matures later. Extracting user accounts can be a difficult barrier for transitions like that.
This looks pretty reasonable! I would love to see a Cloud Storage backend. A minor quibble is that I think that managing your own metrics in Redis is probably not the simplest or most flexible approach - instead, you should consider exposing a /metrics endpoint that can be ingested by the user's monitoring tool of choice (Prometheus/InfluxDB/etc).
Thanks!

Have you seen the /stats endpoint? It exposes the metrics as JSON, which may be a good match for your suggestion. I'd also like to export the key events to a STATSD-compatible sink so a sophisticated user can manage metrics in their own system.

Redis is already on hand because of other features though, and HLL is a pretty cheap integration. I figure it's a decent starting point for many people.

It looks like the /stats endpoint consults the Redis database and returns processed data. If we're aggregating metrics externally, it would be more idiomatic for this to be totally stateless and just expose hot metrics - it's the aggregator's job to store and process the data. This can be quite painless using something like the Go Prometheus library.

Being tied to Redis is also not great. For instance, it looks like everything apart from the metrics could comfortably live in Google Cloud Storage if you had a backend for it. Cloud Storage really isn't an appropriate place to put frequently updated counters, though. So if I deployed AuthN, I would want to be able to ditch Redis.

I should stress, though, that these are quibbles. I think AuthN looks very nice, and the quibbles are cropping up because I'm trying to figure out if I can use it. :)

That all sounds like a direction I'd happily consider:

* Google Cloud Storage implementations for data interfaces

* Metrics interface with a Prometheus implementation (STATSD to follow)

* Redis-backed HLL metrics are optional

Feel free to open issues in the tracker if you reach that point!

Good stuff.

I have one other bit of feedback on the architecture, and I realise that this one might be too different from where AuthN is currently. I see that there are a small number of API endpoints that you've marked "public" that users are meant to be able to reach. In practice, the user's POSTs would always be terminated upstream from the AuthN server itself, either at a gateway or by just relaying the requests through from the front-end servers themselves. A service that's part public/part private is much more perilous than a service that is one or the other. All things considered, these public endpoints seem like a very minor convenience to the integrator. I would just make the AuthN API private, and ask users to implement the public endpoints themselves by making requests to the AuthN API.

If you DID make all endpoints private, you're no longer tied to HTTP, and you have a nice opportunity to use GRPC instead. I've recently started rolling it out in my own services, and it's unexpectedly great considering the track-record of similar ideas. It's very performant, pleasant to work with, has a mature ecosystem of surrounding tools, and you get client API implementations in a wide range of languages for "free" (or at least at a cut price).

My intention for the user-facing endpoints is that the host app will never need to see or accidentally log a user's password. It's a pattern inspired by credit card vaults.

Could you still achieve your deployment goals with a Gateway/WAF setup that proxies the user-facing endpoints? The only issue I'm presently aware of with this setup is https://github.com/keratin/authn-server/issues/8.

Am I missing something, or does this really have no support for TOTP/HOTP? An authentication system without 2FA or U2F support in 2017 seems... lacking (or unfinished).
It's on my roadmap. Prioritizing is hard. :/
How does AuthN compare to Keycloak?

http://www.keycloak.org

Keycloak does some really great things. It does require managing a Java runtime though, and is missing the streamlining that allows AuthN to run as an invisible API.

Keycloak (and similar) hosts and renders your login page. You customize through theming. You're expected to redirect users through a standard OAuth2/OIDC flow on a different domain.

AuthN doesn't render any HTML. That's all you, from start to finish. This means you have control over the UX and can build the login page directly into your own app, just like you would when using an auth library in a typical monolith.

What, I can't simply post to any login/register/logout uri's and expect either a redirect with configuring headers back or an object that let's me manage the token manually?

EDIT: I've been trying out keycloak and it looks great but I've always assumed I just had not figured out how to make that happen. As the documentation is quite large and of the harder kind.

You can design the login/register page in keycloak completely. From scratch. I have used it before and had done that.
the toplevel links (implementation / deployment / configuration) don't work for me, they go to say

/keratin/authn-server/docs/config.md

which is a 404 presumably instead of

/keratin/authn-server/blob/master/docs/config.md

Fixed, thanks!
no problem, btw, the first time I did make test I got this, but after that I was not able to repro it again and make test always worked, pasting here in case you are interested

    Creating authnserver_server_1 ... done
    TEST_REDIS_URL=redis://127.0.0.1:8701/12 \
      TEST_MYSQL_URL=mysql://root@127.0.0.1:8702/authnservertest \
      go test ./data/... ./models/... ./tokens/... ./ops/... ./config/... ./lib/... ./api/... ./services/... .
    [mysql] 2017/11/08 13:27:23 packets.go:33: unexpected EOF
    [mysql] 2017/11/08 13:27:23 packets.go:33: unexpected EOF
    [mysql] 2017/11/08 13:27:23 packets.go:33: unexpected EOF
    --- FAIL: TestAccountStore (0.01s)
        --- FAIL: TestAccountStore/MySQL (0.00s)
            Error Trace:    account_store_test.go:43
        	Error:		Received unexpected error driver: bad connection
        			Connect
        			github.com/keratin/authn-server/data/mysql.ensureDB
        				/home/luser/go/external/src/github.com/keratin/authn-server/data/mysql/db.go:71
        			github.com/keratin/authn-server/data/mysql.TestDB
        				/home/luser/go/external/src/github.com/keratin/authn-server/data/mysql/db.go:27
        			github.com/keratin/authn-server/data_test.TestAccountStore.func3
        				/home/luser/go/external/src/github.com/keratin/authn-server/data/account_store_test.go:42
        			testing.tRunner
        				/usr/local/stow/go-1.9.0/go/src/testing/testing.go:746
        			runtime.goexit
        				/usr/local/stow/go-1.9.0/go/src/runtime/asm_amd64.s:2337
        			ensureDB
        			github.com/keratin/authn-server/data/mysql.TestDB
        				/home/luser/go/external/src/github.com/keratin/authn-server/data/mysql/db.go:29
        			github.com/keratin/authn-server/data_test.TestAccountStore.func3
        				/home/luser/go/external/src/github.com/keratin/authn-server/data/account_store_test.go:42
        			testing.tRunner
        				/usr/local/stow/go-1.9.0/go/src/testing/testing.go:746
        			runtime.goexit
        				/usr/local/stow/go-1.9.0/go/src/runtime/asm_amd64.s:2337
        		
    FAIL
    FAIL	github.com/keratin/authn-server/data	0.040s
    ?   	github.com/keratin/authn-server/data/mock	[no test files]
    ?   	github.com/keratin/authn-server/data/mysql	[no test files]
    ok  	github.com/keratin/authn-server/data/redis	0.771s
    ?   	github.com/keratin/authn-server/data/sqlite3	[no test files]
Thanks for the report! I believe I've tracked this down to an initialization routine that MySQL goes through on the first boot. It happens after docker-compose unblocks. Likely a wontfix. :/
cool you found the root cause, I take this is not the code path that runs in CI? otherwise I'd think you'd hit it all the time unless somehow the MySQL container sticks around...
Yeah. I think Travis provides a warmed server.
Has Keratin gone through any security research / penetration testing?
I would dearly love that! The answer is not yet. Can you recommend any testers that are OSS-friendly?

My current plan is to set up a HackerOne page. I know that bug bounties don't replace good penetration testing, but it's a start.

Is SAML supported and if not is it planned in the near future ?
I'm currently investing in JWT and have not done enough research on SAML to make it part of my plans. Happy to learn more.
You might want to take a look at AWS-Cognito and their SAML and OAuth2.0 integration. For our enterprise startup, we are leaning towards Cognito to provide multi-tenant UserPools with customer specified authentication providers.
Is there an API client for this in Go?
Seems like an oversight, doesn't it? I've created an issue to track here: https://github.com/keratin/authn-server/issues/15
It's just name/pass

It would be much more interesting to me if it also did Oauth2 login with Google/Facebook/Twitter/etc.

Yeah, name/pass sounds pretty simple, doesn't it? But doing it correctly, securely, with a service architecture? That gets interesting.

> It would be much more interesting to me if it also did Oauth2 login with Google/Facebook/Twitter/etc.

Totally agreed. The reason I designed AuthN around accounts first is because I believe that's the best way to launch an app. OAuth2 and OIC logins are powerful, but they're secondary to the classic login.

almost no test coverage. did i miss them ? for proper use in production you would need to have hundreds of unittests and a whole bunch of component + integration and e2e tests.
Tests are colocated inside packages (folders) using a `_test.go` convention.

Service tests[1] are the main unit tests, and use mock implementations of the data store interfaces.

Data (DAO) tests[2] are generally run across every implementation using only the public interface. This helps me stay sane with the mock implementations.

The API tests[3] are integration tests, and use Go's excellent httptest package to boot a real server and execute real HTTP commands.

[1] https://github.com/keratin/authn-server/tree/master/services

[2] https://github.com/keratin/authn-server/tree/master/data

[3] https://github.com/keratin/authn-server/tree/master/api/acco...

There are tests - they're colocated with the source, not in a separate tests folder, e.g. https://github.com/keratin/authn-server/tree/master/services
Check the make file:

https://github.com/keratin/authn-server/blob/master/Makefile...

Notice it runs "go test $(shell glide nv)"

`glide nv` is a command that gets you all packages except the vendor directory https://github.com/Masterminds/glide#glide-novendor-aliased-...

then read the go doc for test https://golang.org/cmd/go/#hdr-Test_packages:

> 'Go test' recompiles each package along with any files with names matching the file pattern "*_test.go".

Yeah, you missed them.
"Microservices perform better, especially when written in Go."

Nonsense.

Please don't post unsubstantive dismissals to HN.

If you have a substantive point to make, make it thoughtfully; if you don't, please don't comment until you do.

This is true for extremely generous definitions of "perform".

Microservices can lead to better performance by making for smaller, more clearly defined codebases, fewer unnecessary imports, and so on. They can also be easy to scale, because you can scale specifically that one component (e.g. identity management) by moving it to a separate database server.

We use microservices, and we have probably close to 2TB of MySQL database, but because we have our services and their database schemas cleanly separated, we can break that up into a set of databases which all fit into memory and one database which, being basically append-only, doesn't need to access historical data and so doesn't need to fit entirely in RAM.

It also lets us easily look at our cluster stats and see where bottlenecks are, by easily seeing which servers or services are under load.

We do pay a penalty for this; layers of indirection, network latency, protocol overhead, serialization/deserialization, and so on, but designing our systems like that from the start lets us tackle those problems at the start and account for them in our design.

Sort of agree, that's a very noisy and broad statement. I'd argue that the underlying I/O and event loop implementation matters more. At the end of the day it's all about highly available systems. Am I wrong?
Microservices need to be divided up very carefully, with full knowledge of the latency trade-off of moving functionality across a network gap. A same-datacenter round-trip takes 5,000 times longer than a main memory access, and 1,000,000 times longer than an L1 cache access. There's no silver bullet in performance or scalability. Each solution will require trading off advantages that matter less to achieve business goals.

Here are a few references that leap to mind (I keep the first printed out and pinned right next to my monitor).

https://gist.github.com/jboner/2841832

https://www.quora.com/What-are-some-mind-blowing-facts-about...

> A same-datacenter round-trip takes 5,000 times longer than a main memory access, and 1,000,000 times longer than an L1 cache access.

This is a ridiculous comparison though. When have you ever seen a situation where a micro service was accessed over the network to fetch something that would have been in L1 cache? And yes, having a value "in memory" is faster, but in most cases these days things aren't "in memory", they're "in memory in memcached, somewhere in the cluster, probably not on the local server so you have to go over the network anyway".

Just out of curiosity though, I tested on our network. Our RTT between servers is less than half a millisecond. Our fastest service can get a cached reply back to the calling service in 35ms, whereas many calls are >100ms and some are much longer (e.g. initial login calls). It would take a lot of external login calls to noticeably (to the user) increase even the fastest service.

We also use Redis and memcached, and spread those across other services. Excluding specifically HTTP overhead and just looking at network latency, a few memcached calls which hit non-local servers in the cluster cost us as much in latency as one HTTP call would.

The gain is decoupling deploy and implementation, not latency performance (or network reliability).
Yep, it's very broad. Let's say it depends how "majestic" a person's monolith is? :D

One point of context I'd like to inject here is that chatter between AuthN and a host app is pretty minimal. Aside from executing admin actions like locking an account, the main dependency is fetching a public key to verify JWTs. This public key can be cached using a standard key fingerprint, which means it only needs to happen once per process.

Architecture does matter, and I've been pretty happy with how AuthN's boundary has played out.

You are right. Architecture is all that matters unless you are in some very special niche.