haskell-servant / servant-auth

160 stars 73 forks source link

How to implement new authorization schemes #119

Open alexjg opened 6 years ago

alexjg commented 6 years ago

I've had a brief read through the source code and it looks like it should be straightforward to implement a new authorization scheme but the classes I would need to implement aren't exposed as part of the public API (for example the IsAuth class). Is this library intended to be extensible in this way or should I just use Servant generalized auth?

In case it's of interest the scheme I'm trying to implement is token authentication but not JWT, just tokens stored in a DB.

alpmestan commented 6 years ago

I have been suspecting this for a while as well but haven't really taken the time to look closer and try and write my own auth scheme. I think we would not mind exposing more things, so feel free to make a PR, unless @domenkozar or @phadej disagree (unlikely though, we all want the servant libraries to be extensible, and anything standing in the way of that needs to be taken care of).

domenkozar commented 6 years ago

@alexjg I think that no one has done that yet, so bits may be missing in the public api. If you come up with a list of what needs exporting, or even better a PR, I see no reason to merge that.

alexjg commented 6 years ago

Great, I'll have a crack at implementing something and pop in an MR with the required changes in a bit.

alexjg commented 6 years ago

So I've just got an auth scheme up and running. The only missing export was the IsAuth class. However, there appear to be some hard coded requirements on the context for the HasServer instance for Auth. Namely this piece of code:

instance ( n ~ 'S ('S 'Z)
         , HasServer (AddSetCookiesApi n api) ctxs, AreAuths auths ctxs v
         , HasServer api ctxs -- this constraint is needed to implement hoistServer
         , AddSetCookies n (ServerT api Handler) (ServerT (AddSetCookiesApi n api) Handler)
         , ToJWT v
         , HasContextEntry ctxs CookieSettings
         , HasContextEntry ctxs JWTSettings
         ) => HasServer (Auth auths v :> api) ctxs where
  type ServerT (Auth auths v :> api) m = AuthResult v -> ServerT api m

This meant I had to add a ToJWT instance for my user type and the cookie and JWT settings to my context. Is there any reason they're hardcoded in here or could they be removed easily?

benperez commented 6 years ago

In case it's of interest the scheme I'm trying to implement is token authentication but not JWT, just tokens stored in a DB.

FWIW, I have done something similar in my application by hijacking the machinery for BasicAuth. BasicAuth already has a type family for specifying your own config for a given type. I made my BasicAuthCfg type instance a custom function which looks in the db for a given token. I just store the token in the password field of the BasicAuth header and look it up in the db as necessary.

data ApiUserWithToken = ApiUserWithToken
  { apiUser :: ApiUser
  , tokenId :: Int64
  } deriving (Eq, Show, Generic, ToJSON, FromJSON)

instance ToJWT ApiUserWithToken
instance FromJWT ApiUserWithToken

type instance BasicAuthCfg = BasicAuthData -> IO (AuthResult ApiUserWithToken)

instance FromBasicAuthData ApiUserWithToken where
  fromBasicAuthData authData checkBasicAuthFunction = checkBasicAuthFunction authData

If you can have your client use this "custom" basic auth this may be an option for you too.

domenkozar commented 6 years ago

See https://github.com/haskell-servant/servant-auth/issues/40

alpmestan commented 6 years ago

@alexjg Yes, this is indeed a bit of code we need to change. We don't want to enforce any auth scheme and we therefore don't want to be paying for what we don't use. So again, you should feel free to change whatever is needed to make your auth scheme work well, and we can see in the PR whether we can get away with less changes (I suspect not, as I had spotted some of those issues already and was suspecting there was 2 or 3 places that needed changing).

alexjg commented 6 years ago

I submitted a PR as discussed https://github.com/haskell-servant/servant-auth/pull/120

Anyone able to take a look at it?

alpmestan commented 6 years ago

Nice! I'll try to take a look later today.

chshersh commented 5 years ago

Hello everyone! Could someone help me to clarify the status of this issue? Is it possible now to add custom auth schemes to servant using servant-auth? I'm working on servant-hmac-auth library which provides HMAC authentication. But it currently uses old experimental approach for auth:

As far as I can see, this approach is going to be deprecated:

Would be really nice to see some tutorial on how to write custom auth schemes using servant-auth library 👍