{"id":13687515,"url":"https://github.com/obsidiansystems/obelisk-oauth","last_synced_at":"2025-07-05T11:35:38.471Z","repository":{"id":53187007,"uuid":"171497870","full_name":"obsidiansystems/obelisk-oauth","owner":"obsidiansystems","description":null,"archived":false,"fork":false,"pushed_at":"2023-07-17T15:47:28.000Z","size":95,"stargazers_count":12,"open_issues_count":7,"forks_count":3,"subscribers_count":31,"default_branch":"master","last_synced_at":"2025-03-26T10:11:38.421Z","etag":null,"topics":["haskell","oauth"],"latest_commit_sha":null,"homepage":null,"language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/obsidiansystems.png","metadata":{"files":{"readme":"README.md","changelog":"ChangeLog.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-02-19T15:24:52.000Z","updated_at":"2023-03-01T03:14:38.000Z","dependencies_parsed_at":"2024-01-14T18:09:21.759Z","dependency_job_id":null,"html_url":"https://github.com/obsidiansystems/obelisk-oauth","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obsidiansystems%2Fobelisk-oauth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obsidiansystems%2Fobelisk-oauth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obsidiansystems%2Fobelisk-oauth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obsidiansystems%2Fobelisk-oauth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/obsidiansystems","download_url":"https://codeload.github.com/obsidiansystems/obelisk-oauth/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248591061,"owners_count":21129952,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["haskell","oauth"],"created_at":"2024-08-02T15:00:55.851Z","updated_at":"2025-04-12T15:45:18.576Z","avatar_url":"https://github.com/obsidiansystems.png","language":"Haskell","funding_links":[],"categories":["Haskell"],"sub_categories":[],"readme":"# obelisk-oauth\n\n## Setup\n\nThis repo contains two packages: `obelisk-oauth-common` and `obelisk-oauth-backend`.\n\nAn example usage of these packages is available in the `example` directory.\n\nTo 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).\n\n### Add dependency thunk\n```bash\n$ mkdir dep\n$ cd dep\n$ git clone git@github.com:obsidian.systems/obelisk-oauth\n$ ob thunk pack obelisk-oauth\n```\n\nThe 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.\n\nCheck out `ob thunk --help` to learn more about working with thunks.\n\n### Add packages to default.nix\n\nYour skeleton project's `default.nix` uses the [reflex-platform project infrastructure](https://github.com/reflex-frp/reflex-platform/blob/develop/project/default.nix). We can use the [`packages` field](https://github.com/reflex-frp/reflex-platform/blob/develop/project/default.nix#L53-L58) of the project configuration to add our custom packages, as follows:\n\n```nix\nproject ./. ({ hackGet, ... }: {\n  packages = {\n    obelisk-oauth-common = (hackGet ./dep/obelisk-oauth) + \"/common\";\n    obelisk-oauth-backend = (hackGet ./dep/obelisk-oauth) + \"/backend\";\n    ... # other configuration goes here\n  };\n})\n```\n\nBe 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.\n\n### Add packages to cabal files\n\nFinally, 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`.\n\n## `Common.Route` + `Obelisk.OAuth.Authorization`\n\nAdd a sub-route to your backend route and embed the provided OAuth route:\n\n```haskell\ndata BackendRoute :: * -\u003e * where\n  BackendRoute_Missing :: BackendRoute ()\n  BackendRoute_Api :: BackendRoute ()\n  BackendRoute_OAuth :: BackendRoute (R OAuth)\n```\n\nYour backend route encoder should handle this case:\n```haskell\n  ...\n  pathComponentEncoder $ \\case\n    BackendRoute_OAuth -\u003e PathSegment \"oauth\" oauthRouteEncoder\n  ...\n```\n\n## Frontend\n\nOn the frontend, you need to produce an authorization request link with the appropriate callback embedded.\n\nFor example:\n\n```haskell\ndo\n  let r = AuthorizationRequest\n        { _authorizationRequest_responseType = AuthorizationResponseType_Code\n        , _authorizationRequest_clientId = clientId\n        , _authorizationRequest_redirectUri = Just BackendRoute_OAuth\n        , _authorizationRequest_scope = []\n        , _authorizationRequest_state = Just \"none\"\n        }\n      grantHref = authorizationRequestHref \"https://app.asana.com/-/oauth_authorize\" route checkedEncoder r\n  elAttr \"a\" (\"href\" =: grantHref) $ text \"Authorize with Asana\"\n```\n\n## Backend\n\nIn your backend handler, you'll need to handle the OAuth sub-route you created:\n\n```haskell\n...\nserve $ \\case\n  BackendRoute_OAuth :/ oauthRoute -\u003e case oauthRoute of\n    OAuth_RedirectUri :/ redirectParams -\u003e case redirectParams of\n      Nothing -\u003e liftIO $ error \"Expected to receive the authorization code here\"\n      Just (RedirectUriParams code mstate) -\u003e do\n        let t = TokenRequest\n              { _tokenRequest_grant = TokenGrant_AuthorizationCode $ T.encodeUtf8 code\n              , _tokenRequest_clientId = clientId -- Get this from the OAuth authorization server\n              , _tokenRequest_clientSecret = clientSecret -- Get this from the OAuth authorization server\n              , _tokenRequest_redirectUri = BackendRoute_OAuth\n              }\n            reqUrl = \"https://app.asana.com/-/oauth_token\"\n        rsp \u003c- liftIO $ flip httpLbs tlsMgr =\u003c\u003c getOauthToken reqUrl route checkedEncoder t\n        -- ^ this response should include the access token and probably a refresh token\n...\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobsidiansystems%2Fobelisk-oauth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fobsidiansystems%2Fobelisk-oauth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobsidiansystems%2Fobelisk-oauth/lists"}