This repo contains two packages: obelisk-oauth-common
and obelisk-oauth-backend
.
An example usage of these packages is available in the example
directory.
To add these packages to your obelisk project, follow the steps below from your obelisk project root (i.e., the folder you ran ob init
in).
$ mkdir dep
$ cd dep
$ git clone [email protected]:obsidian.systems/obelisk-oauth
$ ob thunk pack obelisk-oauth
The last step here (ob thunk pack
) replaces the cloned repository with a "thunk" that contains all the information obelisk needs to fetch/use the repository when needed.
Check out ob thunk --help
to learn more about working with thunks.
Your skeleton project's default.nix
uses the reflex-platform project infrastructure. We can use the packages
field of the project configuration to add our custom packages, as follows:
project ./. ({ hackGet, ... }: {
packages = {
obelisk-oauth-common = (hackGet ./dep/obelisk-oauth) + "/common";
obelisk-oauth-backend = (hackGet ./dep/obelisk-oauth) + "/backend";
... # other configuration goes here
};
})
Be sure to add hackGet
to the list of items to bring into scope. hackGet
is a nix function defined in reflex-platform that takes a path that points to either a source directory or a packed thunk (in other words, it takes a path to a thunk but doesn't care whether it's packed or unpacked). It produces a path to the source (unpacked if necessary). Once we've got that path, we just need to append the subdirectory paths to the individual repos contained in this repository.
Finally, add obelisk-oauth-common
to the build-depends
field of common/common.cabal
and add obelisk-oauth-common
and obelisk-oauth-backend
to the build-depends
field of the library stanza in backend/backend.cabal
.
Add a sub-route to your backend route and embed the provided OAuth route:
data BackendRoute :: * -> * where
BackendRoute_Missing :: BackendRoute ()
BackendRoute_Api :: BackendRoute ()
BackendRoute_OAuth :: BackendRoute (R OAuth)
Your backend route encoder should handle this case:
...
pathComponentEncoder $ \case
BackendRoute_OAuth -> PathSegment "oauth" oauthRouteEncoder
...
On the frontend, you need to produce an authorization request link with the appropriate callback embedded.
For example:
do
let r = AuthorizationRequest
{ _authorizationRequest_responseType = AuthorizationResponseType_Code
, _authorizationRequest_clientId = clientId
, _authorizationRequest_redirectUri = Just BackendRoute_OAuth
, _authorizationRequest_scope = []
, _authorizationRequest_state = Just "none"
}
grantHref = authorizationRequestHref "https://app.asana.com/-/oauth_authorize" route checkedEncoder r
elAttr "a" ("href" =: grantHref) $ text "Authorize with Asana"
In your backend handler, you'll need to handle the OAuth sub-route you created:
...
serve $ \case
BackendRoute_OAuth :/ oauthRoute -> case oauthRoute of
OAuth_RedirectUri :/ redirectParams -> case redirectParams of
Nothing -> liftIO $ error "Expected to receive the authorization code here"
Just (RedirectUriParams code mstate) -> do
let t = TokenRequest
{ _tokenRequest_grant = TokenGrant_AuthorizationCode $ T.encodeUtf8 code
, _tokenRequest_clientId = clientId -- Get this from the OAuth authorization server
, _tokenRequest_clientSecret = clientSecret -- Get this from the OAuth authorization server
, _tokenRequest_redirectUri = BackendRoute_OAuth
}
reqUrl = "https://app.asana.com/-/oauth_token"
rsp <- liftIO $ flip httpLbs tlsMgr =<< getOauthToken reqUrl route checkedEncoder t
-- ^ this response should include the access token and probably a refresh token
...