Browse Source

Merge branch 'feature/add-oauth2-flow' into develop

* feature/add-oauth2-flow:
  chore(project): Add copy and footer.
  chore(oauth): Log into users.
  chore(project): Update typefaces.
  chore(oauth2): Add logic for GitHub prereq.
  chore(oauth): Add router and controller.
jackyalcine 2 months ago
parent
commit
933a421bd2
Signed by: Jacky Alciné <yo@jacky.wtf> GPG Key ID: 537A4F904B15268D

+ 32
- 16
assets/.eslintrc.js View File

@@ -1,17 +1,33 @@
1 1
 module.exports = {
2
-  'env': {
3
-    'browser': true,
4
-    'es6': true
5
-  },
6
-  'extends': 'standard',
7
-  'globals': {
8
-    'Atomics': 'readonly',
9
-    'SharedArrayBuffer': 'readonly'
10
-  },
11
-  'parserOptions': {
12
-    'ecmaVersion': 2018,
13
-    'sourceType': 'module'
14
-  },
15
-  'rules': {
16
-  }
17
-}
2
+    'env': {
3
+        'browser': true,
4
+        'es6': true
5
+    },
6
+    'extends': 'eslint:recommended',
7
+    'globals': {
8
+        'Atomics': 'readonly',
9
+        'SharedArrayBuffer': 'readonly'
10
+    },
11
+    'parserOptions': {
12
+        'ecmaVersion': 2018,
13
+        'sourceType': 'module'
14
+    },
15
+    'rules': {
16
+        'indent': [
17
+            'error',
18
+            4
19
+        ],
20
+        'linebreak-style': [
21
+            'error',
22
+            'unix'
23
+        ],
24
+        'quotes': [
25
+            'error',
26
+            'single'
27
+        ],
28
+        'semi': [
29
+            'error',
30
+            'never'
31
+        ]
32
+    }
33
+}

+ 37
- 37
assets/app.ts View File

@@ -6,64 +6,64 @@ import * as Feather from 'feather-icons'
6 6
 require('phoenix_html');
7 7
 
8 8
 [
9
-  ['credit-card', 'patreon'], ['key', 'keybase'], ['align-left', 'medium']
9
+    ['credit-card', 'patreon'], ['key', 'keybase'], ['align-left', 'medium'], ['check-square', 'flash-success'], ['alert-triangle', 'flash-warning'], ['info', 'flash-info'], ['x-octagon', 'flash-error'], ['at-sign', 'indieauth']
10 10
 ].forEach((mapping) => {
11
-  Feather.icons[mapping[1]] = Feather.icons[mapping[0]]
11
+    Feather.icons[mapping[1]] = Feather.icons[mapping[0]]
12 12
 })
13 13
 
14 14
 function hideLoader () {
15
-  document.getElementById('loader').classList.add('dn')
15
+    document.getElementById('loader').classList.add('dn')
16 16
 }
17 17
 
18 18
 function showLoader () {
19
-  document.getElementById('loader').classList.remove('dn')
19
+    document.getElementById('loader').classList.remove('dn')
20 20
 }
21 21
 
22 22
 function buildLiveSocket () {
23
-  let liveSocket = new LiveSocket('/live', Socket, {
24
-    hooks: {
25
-      Generic: {
26
-        mounted () {
27
-          console.log('woah: mounted', this.el, this.viewName)
28
-          hideLoader()
29
-        },
30
-        disconnected () {
31
-          console.log('woah: disc', this.el, this.viewName)
32
-          showLoader()
33
-        },
34
-        reconnected () {
35
-          console.log('woah: rec', this.el, this.viewName)
36
-          hideLoader()
37
-        },
38
-        connected () {
39
-          console.log('woah: conn', this.el, this.viewName)
40
-          hideLoader()
23
+    let liveSocket = new LiveSocket('/live', Socket, {
24
+        hooks: {
25
+            Generic: {
26
+                mounted () {
27
+                    console.log('woah: mounted', this.el, this.viewName)
28
+                    hideLoader()
29
+                },
30
+                disconnected () {
31
+                    console.log('woah: disc', this.el, this.viewName)
32
+                    showLoader()
33
+                },
34
+                reconnected () {
35
+                    console.log('woah: rec', this.el, this.viewName)
36
+                    hideLoader()
37
+                },
38
+                connected () {
39
+                    console.log('woah: conn', this.el, this.viewName)
40
+                    hideLoader()
41
+                }
42
+            }
41 43
         }
42
-      }
43
-    }
44
-  })
44
+    })
45 45
 
46
-  liveSocket.connect()
47
-  return liveSocket
46
+    liveSocket.connect()
47
+    return liveSocket
48 48
 }
49 49
 
50 50
 window.addEventListener('load', () => {
51
-  Feather.replace()
52
-  WebFont.load({
53
-    google: {
54
-      families: ['Megrim', 'Playfair Display', 'Open Sans']
55
-    }
56
-  })
57
-  buildLiveSocket()
51
+    Feather.replace()
52
+    WebFont.load({
53
+        google: {
54
+            families: ['Oswald', 'Playfair Display', 'Open Sans', 'Droid Mono']
55
+        }
56
+    })
57
+    buildLiveSocket()
58 58
 })
59 59
 
60 60
 let timeoutForRebuild = 0
61 61
 
62 62
 document.addEventListener('phx:update', () => {
63
-  clearTimeout(timeoutForRebuild)
64
-  timeoutForRebuild = setTimeout(rebuildPage, 500)
63
+    clearTimeout(timeoutForRebuild)
64
+    timeoutForRebuild = setTimeout(rebuildPage, 500)
65 65
 })
66 66
 
67 67
 function rebuildPage () {
68
-  Feather.replace()
68
+    Feather.replace()
69 69
 }

+ 12
- 12
assets/package-lock.json View File

@@ -2889,6 +2889,15 @@
2889 2889
         "node-addon-api": "^1.6.0"
2890 2890
       }
2891 2891
     },
2892
+    "debug": {
2893
+      "version": "4.1.1",
2894
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
2895
+      "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
2896
+      "dev": true,
2897
+      "requires": {
2898
+        "ms": "^2.1.1"
2899
+      }
2900
+    },
2892 2901
     "decamelize": {
2893 2902
       "version": "1.2.0",
2894 2903
       "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
@@ -3385,15 +3394,6 @@
3385 3394
         "text-table": "^0.2.0"
3386 3395
       },
3387 3396
       "dependencies": {
3388
-        "debug": {
3389
-          "version": "4.1.1",
3390
-          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
3391
-          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
3392
-          "dev": true,
3393
-          "requires": {
3394
-            "ms": "^2.1.1"
3395
-          }
3396
-        },
3397 3397
         "semver": {
3398 3398
           "version": "5.7.1",
3399 3399
           "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
@@ -5126,9 +5126,9 @@
5126 5126
       "dev": true
5127 5127
     },
5128 5128
     "import-fresh": {
5129
-      "version": "3.1.0",
5130
-      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz",
5131
-      "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==",
5129
+      "version": "3.2.1",
5130
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
5131
+      "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
5132 5132
       "dev": true,
5133 5133
       "requires": {
5134 5134
         "parent-module": "^1.0.0",

+ 43
- 2
assets/site.scss View File

@@ -1,9 +1,10 @@
1 1
 @import "tachyons-sass/tachyons.scss";
2 2
 
3 3
 :root {
4
-  --font-flagship: "Megrim";
4
+  --font-flagship: "Oswald";
5 5
   --font-serif: "Playfair Display";
6 6
   --font-sans: "Open Sans";
7
+  --font-mono: "Droid Mono";
7 8
 }
8 9
 
9 10
 .font--flagship {
@@ -18,6 +19,10 @@
18 19
   font-family: var(--font-sans);
19 20
 }
20 21
 
22
+.code, pre, code {
23
+  font-family: var(--font-mono);
24
+}
25
+
21 26
 svg.feather.spin {
22 27
   animation: 0.8s linear infinite spin;
23 28
   animation-play-state: running;
@@ -44,7 +49,7 @@ div.provider--status {
44 49
       @extend .pointer, .o-50, .glow;
45 50
     }
46 51
   }
47
-  
52
+
48 53
   a { @extend .color-inherit, .link; }
49 54
 
50 55
   &[data-status="pending"] {
@@ -71,3 +76,39 @@ div.provider--info {
71 76
     @extend .v-mid, .color-inherit, .f7, .pv1;
72 77
   }
73 78
 }
79
+
80
+div.flash {
81
+  @extend .w-100, .pa2;
82
+
83
+  > p {
84
+    @extend .mw7, .w-100, .center;
85
+  }
86
+
87
+  svg.feather {
88
+    @extend .v-mid, .w1, .h1, .ma1;
89
+  }
90
+}
91
+
92
+div.flash--success {
93
+  @extend .bg-light-green, .dark-green;
94
+}
95
+
96
+body >  header {
97
+  @extend .bg-near-white, .near-black, .w-100;
98
+
99
+  .p-note {
100
+    @extend .f7;
101
+  }
102
+
103
+  > div {
104
+    @extend .inline-flex, .flex, .flex-column, .items-center, .justify-center;
105
+  }
106
+
107
+  nav {
108
+    @extend .f5, .mt3, .mt0-l;
109
+
110
+    a {
111
+      @extend .link, .color-inherit, .dark-gray, .hover-navy, .fw6, .pa2;
112
+    }
113
+  }
114
+}

+ 6
- 3
config/config.exs View File

@@ -12,7 +12,7 @@ config :fortress, Fortress.Web.Endpoint,
12 12
     signing_salt:
13 13
       {:system, :string, "LIVEVIEW_SECRET",
14 14
        64 |> :crypto.strong_rand_bytes() |> Base.encode64(case: :lower, padding: false)}
15
-        ],
15
+  ],
16 16
   secret_key_base:
17 17
     {:system, :string, "SECRET_KEY_BASE",
18 18
      64 |> :crypto.strong_rand_bytes() |> Base.encode64(case: :lower, padding: false)},
@@ -25,7 +25,7 @@ config :fortress, Fortress.Guardian,
25 25
   secret_key:
26 26
     {:system, :string, "GUARDIAN_SECRET_KEY", :crypto.strong_rand_bytes(32) |> Base.encode64()},
27 27
   schema_name: "guardian_tokens",
28
-  sweep_interval: 60
28
+  sweep_interval: 30
29 29
 
30 30
 config :fortress, Fortress.Repo,
31 31
   url: System.get_env("DATABASE_URL"),
@@ -42,13 +42,16 @@ config :mnesia, dir: 'priv/mnesia/#{Mix.env()}/#{node()}'
42 42
 config :ueberauth, Ueberauth,
43 43
   base_path: "/oauth/",
44 44
   providers: [
45
-    github: {Ueberauth.Strategy.Github, [default_scope: "read:user"]}
45
+    github: {Ueberauth.Strategy.Github, [default_scope: "user:email"]},
46
+    indieauth: {Ueberauth.Strategy.IndieAuth, [client_id: "https://fortress.ngrok.io"]}
46 47
   ]
47 48
 
48 49
 config :ueberauth, Ueberauth.Strategy.Github.OAuth,
49 50
   client_id: {:system, :string, "GITHUB_CLIENT_ID"},
50 51
   client_secret: {:system, :string, "GITHUB_CLIENT_SECRET"}
51 52
 
53
+config :ueberauth, Ueberauth.Strategy.IndieAuth.OAuth2, []
54
+
52 55
 config :guardian, Guardian.DB, repo: Fortress.Repo
53 56
 
54 57
 config :indieweb, http_adapter: Fortress.Http.Adapter

+ 3
- 1
lib/application.ex View File

@@ -23,7 +23,9 @@ defmodule Fortress.Application do
23 23
       :logger,
24 24
       :fortress,
25 25
       :phoenix,
26
-      :ueberauth
26
+      :ueberauth,
27
+      :indieweb,
28
+      :ueberauth_indieauth
27 29
     ]
28 30
     |> Enum.each(&Confex.resolve_env!/1)
29 31
   end

+ 26
- 3
lib/provider.ex View File

@@ -20,10 +20,26 @@ defmodule Fortress.Provider do
20 20
   def fetch_for(url) do
21 21
     with(
22 22
       rel_me_urls when is_list(rel_me_urls) <- IndieWeb.LinkRel.find(url, "me"),
23
+      indieauth_endpoint <- IndieWeb.LinkRel.find(url, "authorization_endpoint"),
23 24
       providers <-
24 25
         rel_me_urls |> Enum.map(&do_resolve_to_provider(&1, url)) |> Enum.reject(&is_nil/1)
25 26
     ) do
26
-      providers
27
+      indieauth_provider =
28
+        if indieauth_endpoint != [] do
29
+          [
30
+            %Fortress.Provider.Instance{
31
+              platform: :indieauth,
32
+              me: url,
33
+              url: URI.parse(List.first(indieauth_endpoint)),
34
+              form: :federation,
35
+              status: "found"
36
+            }
37
+          ]
38
+        else
39
+          []
40
+        end
41
+
42
+      providers ++ indieauth_provider
27 43
     else
28 44
       {:error, _} = error -> error
29 45
       [] -> {:error, :no_providers_found}
@@ -46,7 +62,7 @@ defmodule Fortress.Provider do
46 62
   def check_for(%Instance{form: :silo} = sioled_instance),
47 63
     do: Fortress.Provider.Silo.check_for(sioled_instance)
48 64
 
49
-  def check_for(%Instance{form: :federated} = federated_instance),
65
+  def check_for(%Instance{form: :federation} = federated_instance),
50 66
     do: Fortress.Provider.Federation.check_for(federated_instance)
51 67
 
52 68
   def broadcast(instance) do
@@ -54,6 +70,13 @@ defmodule Fortress.Provider do
54 70
     Phoenix.PubSub.broadcast(Fortress.PubSub, Instance.topic_for(instance), instance)
55 71
   end
56 72
 
73
+  def supported?(platform),
74
+    do:
75
+      Enum.member?(
76
+        Fortress.Provider.Silo.supported() ++ Fortress.Provider.Federation.supported(),
77
+        String.to_atom(platform)
78
+      )
79
+
57 80
   defp do_resolve_to_provider(url, me)
58 81
 
59 82
   defp do_resolve_to_provider(url, me) when is_binary(url),
@@ -82,7 +105,7 @@ defmodule Fortress.Provider do
82 105
       platform: :gitlab,
83 106
       me: me,
84 107
       url: url,
85
-      form: :federated,
108
+      form: :federation,
86 109
       status: "pending"
87 110
     }
88 111
 

+ 14
- 0
lib/provider/federation.ex View File

@@ -1,4 +1,18 @@
1 1
 defmodule Fortress.Provider.Federation do
2 2
   def check_for(instance)
3
+
4
+  def check_for(%Fortress.Provider.Instance{platform: :indieauth} = instance) do
5
+    instance_url = URI.to_string(instance.url)
6
+    resolved_mes = IndieWeb.LinkRel.find(instance.me, "authorization_endpoint")
7
+
8
+    status = if Enum.member?(resolved_mes, instance_url) do
9
+      "found"
10
+    else
11
+      "broken"
12
+    end
13
+        %{instance | status: status}
14
+  end
3 15
   def check_for(instance), do: %{instance | status: "broken"}
16
+
17
+  def supported(), do: ~w(mastodon pixelfed pleroma indieauth)a
4 18
 end

+ 2
- 0
lib/provider/silo.ex View File

@@ -16,4 +16,6 @@ defmodule Fortress.Provider.Silo do
16 16
   end
17 17
 
18 18
   def check_for(instance), do: %{instance | status: "broken"}
19
+
20
+  def supported(), do: @friendly_silo
19 21
 end

+ 3
- 5
mix.exs View File

@@ -35,7 +35,6 @@ defmodule Fortress.Mixfile do
35 35
   defp deps do
36 36
     [
37 37
       {:apex, "~> 1.0"},
38
-      # {:cachex, "~> 3.0"},
39 38
       {:certifi, "~> 2.4"},
40 39
       {:confex, "~> 3.4.0"},
41 40
       {:calendar, "~> 1.0"},
@@ -46,10 +45,9 @@ defmodule Fortress.Mixfile do
46 45
       {:floki, "~> 0.20.0"},
47 46
       {:faker, "~> 0.12.0", only: :test},
48 47
       {:gettext, "~> 0.11"},
49
-      {:guardian, "~> 1.2.1"},
48
+      {:guardian, "~> 2.0", override: true},
50 49
       {:guardian_db, "~> 2.0.0"},
51 50
       {:guardian_trackable, "~> 0.2.0"},
52
-      # {:hackney, "~> 1.14.0"},
53 51
       {:jason, ">= 1.0.0"},
54 52
       {:logster, "~> 0.4"},
55 53
       {:oauth, github: "tim/erlang-oauth"},
@@ -74,8 +72,8 @@ defmodule Fortress.Mixfile do
74 72
       {:tesla_request_id, "~> 0.2.0"},
75 73
       {:ueberauth, "~> 0.6.1"},
76 74
       {:indieweb, git: "https://git.jacky.wtf/indieweb/elixir", branch: :develop, override: true},
77
-      {:ueberauth_indieauth,
78
-       git: "https://git.jacky.wtf/indieweb/ueberauth_indieauth/", branch: :develop}
75
+      {:ueberauth_indieauth, git: "https://git.jacky.wtf/indieweb/ueberauth_indieauth", branch: :develop},
76
+      {:ueberauth_github, "~> 0.7"},
79 77
     ]
80 78
   end
81 79
 

+ 3
- 2
mix.lock View File

@@ -20,7 +20,7 @@
20 20
   "file_system": {:hex, :file_system, "0.2.7", "e6f7f155970975789f26e77b8b8d8ab084c59844d8ecfaf58cbda31c494d14aa", [:mix], [], "hexpm"},
21 21
   "floki": {:hex, :floki, "0.20.4", "be42ac911fece24b4c72f3b5846774b6e61b83fe685c2fc9d62093277fb3bc86", [:mix], [{:html_entities, "~> 0.4.0", [hex: :html_entities, repo: "hexpm", optional: false]}, {:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
22 22
   "gettext": {:hex, :gettext, "0.17.1", "8baab33482df4907b3eae22f719da492cee3981a26e649b9c2be1c0192616962", [:mix], [], "hexpm"},
23
-  "guardian": {:hex, :guardian, "1.2.1", "bdc8dd3dbf0fb7216cb6f91c11831faa1a64d39cdaed9a611e37f2413e584983", [:mix], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.3", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm"},
23
+  "guardian": {:hex, :guardian, "2.0.0", "5d3e537832b7cf35c8674da92457b7be671666a2eff4bf0f2ccfcfb3a8c67a0b", [:mix], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm"},
24 24
   "guardian_db": {:hex, :guardian_db, "2.0.2", "6247303fda5ed90e19ea1d2e4c5a65b13f58cc12810f95f71b6ffb50ef2d057f", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.1.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:guardian, "~> 1.0 or ~> 2.0", [hex: :guardian, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm"},
25 25
   "guardian_trackable": {:hex, :guardian_trackable, "0.2.1", "1ab8c6055969ad3312c289cb691274abbd601dd45fcdf94ac38a39b8603121db", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:guardian, "~> 1.0", [hex: :guardian, repo: "hexpm", optional: false]}], "hexpm"},
26 26
   "hackney": {:hex, :hackney, "1.15.2", "07e33c794f8f8964ee86cebec1a8ed88db5070e52e904b8f12209773c1036085", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.5", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
@@ -63,7 +63,8 @@
63 63
   "timex": {:hex, :timex, "3.6.1", "efdf56d0e67a6b956cc57774353b0329c8ab7726766a11547e529357ffdc1d56", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
64 64
   "tzdata": {:hex, :tzdata, "1.0.2", "6c4242c93332b8590a7979eaf5e11e77d971e579805c44931207e32aa6ad3db1", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
65 65
   "ueberauth": {:hex, :ueberauth, "0.6.2", "25a31111249d60bad8b65438b2306a4dc91f3208faa62f5a8c33e8713989b2e8", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
66
-  "ueberauth_indieauth": {:git, "https://git.jacky.wtf/indieweb/ueberauth_indieauth/", "73359386d5cadb7e7625084f8f520eb3675b5e09", [branch: :develop]},
66
+  "ueberauth_github": {:hex, :ueberauth_github, "0.7.0", "637067c5500f7b13c18caca3db66d09eba661524e0d0e9518b54151e99484bad", [:mix], [{:oauth2, "~> 0.9", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.4", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm"},
67
+  "ueberauth_indieauth": {:git, "https://git.jacky.wtf/indieweb/ueberauth_indieauth", "73359386d5cadb7e7625084f8f520eb3675b5e09", [branch: :develop]},
67 68
   "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"},
68 69
   "unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm"},
69 70
 }

+ 36
- 0
web/controllers/oauth2.ex View File

@@ -0,0 +1,36 @@
1
+defmodule Fortress.Web.OAuth2Controller do
2
+  use Fortress.Web, :controller
3
+  plug(Ueberauth)
4
+
5
+  alias Ueberauth.Strategy.Helpers
6
+
7
+  def callback(%{assigns: %{ueberauth_failure: _fails}} = conn, _params) do
8
+    # TODO: Navigate to callback.
9
+    conn
10
+    |> put_flash(:error, "Failed to authenticate.")
11
+    |> redirect(to: "/")
12
+  end
13
+
14
+  def callback(%{assigns: %{ueberauth_auth: auth}} = conn, _params) do
15
+    user_url = get_session(conn, :requesting_user)
16
+
17
+    with(
18
+      {:ok, hcard} <- IndieWeb.HCard.fetch_representative(user_url),
19
+      {:ok, site} <- Fortress.Repo.Site.upsert(hcard)
20
+    ) do
21
+      conn
22
+      |> delete_session(:requesting_user)
23
+      |> delete_session(:requested_profile)
24
+      |> put_flash(:success, "We got you, son.")
25
+      |> Fortress.Guardian.Plug.sign_in(site)
26
+      |> redirect(to: "/")
27
+    else
28
+      _ ->
29
+        conn
30
+        |> delete_session(:requesting_user)
31
+        |> delete_session(:requested_profile)
32
+        |> put_flash(:warning, "We couldn't resolve your original h-card.")
33
+        |> redirect(to: "/")
34
+    end
35
+  end
36
+end

+ 16
- 1
web/controllers/resolver.ex View File

@@ -8,7 +8,22 @@ defmodule Fortress.Web.ResolverController do
8 8
     live_render(conn, Fortress.Web.ResolverLiveView, session: %{url: url})
9 9
   end
10 10
 
11
-  def present(conn, _) do
11
+  def present(conn, _), do:
12 12
     render(conn, "present-no_url.html")
13
+
14
+  def request(conn, params)
15
+  def request(conn, %{"platform" => platform, "url" => url, "me" => me} = params) when is_binary(url) and is_binary(me) do
16
+    # TODO: Validate if we support platform.
17
+    cond do
18
+      !Fortress.Provider.supported?(platform) -> render(conn, "request-unsupported.html")
19
+      true ->
20
+        # TODO: Redirect to authorization page with session primed.
21
+        state = 32 |> :crypto.strong_rand_bytes() |> Base.encode64(case: :lower, padding: false)
22
+        conn
23
+        |> put_session(:requested_profile, url)
24
+        |> put_session(:requesting_user, me)
25
+        |> redirect(to: Routes.oauth2_path(conn, :request, platform, me: me, state: state))
26
+    end
13 27
   end
28
+  def request(conn, _), do: render(conn, "request-invalid.html")
14 29
 end

+ 1
- 1
web/fortress.ex View File

@@ -16,7 +16,7 @@ defmodule Fortress.Web do
16 16
         namespace: Fortress.Web
17 17
 
18 18
       # Import convenience functions from controllers
19
-      import Phoenix.Controller, only: [get_flash: 2, view_module: 1]
19
+      import Phoenix.Controller, only: [get_flash: 1, get_flash: 2, view_module: 1]
20 20
       import Phoenix.HTML.SimplifiedHelpers.Truncate
21 21
 
22 22
       import Phoenix.LiveView,

+ 7
- 0
web/helpers/auth_pipeline.ex View File

@@ -0,0 +1,7 @@
1
+defmodule Fortress.Web.Plug.AuthPipeline do
2
+  use Guardian.Plug.Pipeline, otp_app: :fortress
3
+
4
+  plug Guardian.Plug.VerifySession, key: :fortress
5
+  plug Guardian.Plug.VerifyCookie, key: :fortress
6
+  plug Guardian.Plug.LoadResource, allow_blank: true
7
+end

+ 17
- 2
web/router.ex View File

@@ -15,19 +15,34 @@ defmodule Fortress.Web.Router do
15 15
     plug(:accepts, ["json"])
16 16
   end
17 17
 
18
+  pipeline :auth do
19
+    plug Fortress.Web.Plug.AuthPipeline
20
+  end
21
+
22
+  pipeline :ensure_logged_in do
23
+    plug(Guardian.Plug.EnsureAuthenticated, key: :fortress)
24
+  end
25
+
18 26
   scope "/", Fortress.Web do
19
-    pipe_through([:browser])
27
+    pipe_through([:browser, :auth])
20 28
 
21 29
     get("/", PageController, :index)
22 30
   end
23 31
 
24 32
   scope "/", Fortress.Web do
25
-    pipe_through([:browser])
33
+    pipe_through([:browser, :auth])
26 34
 
27 35
     get("/resolve", ResolverController, :present)
28 36
     get("/resolve/via/:platform", ResolverController, :request)
29 37
   end
30 38
 
39
+  scope "/oauth", Fortress.Web do
40
+    pipe_through([:browser, :auth])
41
+    get("/:provider", OAuth2Controller, :request, as: :oauth2)
42
+    get("/:provider/callback", OAuth2Controller, :callback, as: :oauth2)
43
+    post("/:provider/callback", OAuth2Controller, :callback, as: :oauth2)
44
+  end
45
+
31 46
   # Meant for the browser, requires pre-auth by the user.
32 47
   # [:browser, :owner, :requires_owner_auth]
33 48
   scope "/endpoints/indieauth", Fortress.Web do

+ 1
- 1
web/templates/component/provider.html.leex View File

@@ -7,7 +7,7 @@
7 7
   <%= if @instance.status != "found" do %>
8 8
   <i data-rel-me="<%= URI.to_string(@instance.url) %>" phx-click="refresh" data-feather="refresh-cw"></i>
9 9
 <% else %>
10
-  <%= live_link to: Routes.resolver_path(@socket, :request, @instance.platform, me: @instance.me, url: URI.to_string(@instance.url)) do %>
10
+  <%= link to: Routes.resolver_path(@socket, :request, @instance.platform, me: @instance.me, url: URI.to_string(@instance.url)) do %>
11 11
     <i data-feather="play"></i>
12 12
   <% end %>
13 13
 <% end %>

+ 34
- 11
web/templates/layout/app.html.eex View File

@@ -1,5 +1,5 @@
1 1
 <!DOCTYPE html>
2
-<html lang="en" class="no-js">
2
+<html lang="en">
3 3
   <head>
4 4
     <meta charset="utf-8">
5 5
     <meta http-equiv="X-UA-Compatible" content="IE=edge" />
@@ -11,33 +11,56 @@
11 11
     <link rel="me" href="https://black.af" />
12 12
   </head>
13 13
   <body phx-hook="Generic">
14
-    <header class="w-100 near-black bg-near-white">
14
+    <aside class="w-100 flex flex-column">
15
+      <%= for {type, message} <- get_flash(@conn) do %>
16
+        <div class="flash flash--<%= type %>">
17
+          <p>
18
+            <i data-feather="flash-<%= type %>"></i>
19
+            <%= message %>
20
+          </p>
21
+        </div>
22
+      <% end %>
23
+    </aside>
24
+    <header>
15 25
       <div class="mw7 w-100 center flex flex-row-l flex-column flex-wrap items-center justify-between pa4">
16
-        <div class="inline-flex flex-column">
17
-          <a title="Fortress" href="/" class="h-app h-x-app u-url near-black hover-gold f2 link fw9 font--flagship">
26
+        <aside class="inline-flex flex-column items-center justify-center">
27
+          <a title="Fortress" href="/" class="h-app h-x-app u-url near-black hover-light-blue f2 link fw9 font--flagship tracked">
18 28
             <b class="p-name ttu">Fortress</b>
19 29
           </a>
20
-          <span class="p-note f7">
30
+          <span class="p-note">
21 31
             Enhanced logins for the <a class="color-inherit link fw9" href="https://indieweb.org">IndieWeb</a>
22 32
           </span>
23
-        </div>
24
-        <div class="f5 mt3 mt0-l">
25
-          <a href="#" class="link dark-gray hover-navy fw3 pa2">
33
+        </aside>
34
+        <nav>
35
+          <a href="#">
26 36
             Apps
27 37
           </a>
28
-          <a href="#" class="link dark-gray hover-navy fw3 pa2">
38
+          <a href="#">
29 39
             Code
30 40
           </a>
31
-          <a href="#" class="link dark-gray hover-navy fw3 pa2">
41
+          <a href="#">
32 42
             Platforms
33 43
           </a>
34
-        </div>
44
+      </div>
35 45
       </div>
36 46
       <div id="loader" class="absolute right-2 top-2 br-pill shadow-2 bg-dark-gray near-white pa3 dn">
37 47
         Loading
38 48
       </div>
39 49
     </header>
40 50
     <%= render @view_module, @view_template, assigns %>
51
+    <footer class="bg-near-black white-80 pv5 pv6-l ph4">
52
+      <p class="f6">
53
+        <span class="dib mr4 mr5-ns">
54
+          <a href="/" class="pv1 ttu link color-inherit db">Fortress</strong></a>
55
+          by <a class="link underline color-inherit b" href="https://black.af">black.af</a> (&copy; 2018 - 2019)
56
+          <div>Envrionment: <strong><%= Mix.env %></strong></div>
57
+        </span>
58
+        <a class="link underline ttu white-80 hover-light-purple" href="https://black.af/terms/use">Terms</a><span class="gray"> / </span>
59
+        <a class="link underline ttu white-80 hover-gold" href="https://black.af/terms/privacy">Privacy</a><span class="gray"> / </span>
60
+        <a rel="me" class="link underline white-80 hover-green" href="mailto:support@black.af">support@black.af</a>
61
+      </p>
62
+    </footer>
63
+
41 64
     <script async defer type="application/javascript" src="<%= Routes.static_path(@conn, "/assets/app.js") %>"></script>
42 65
   </body>
43 66
 </html>

+ 23
- 3
web/templates/page/index.html.eex View File

@@ -1,11 +1,31 @@
1 1
 <main class="w-100 bg-near-white near-black pa2">
2 2
   <div class="flex flex-column items-start justify-start mw7 center pv3">
3
-    <h1 class="f1 f-headline-l measure fw1 sans dark-gray tracked-tight lh-title">
3
+    <h1 class="f1 f-headline-l measure fw1 dark-gray tracked-tight lh-title">
4 4
       Log In As You.
5 5
     </h1>
6
-    <h3 class="f5 f3-l fw3 sans gray mt0 lh-copy">
6
+    <p class="f4 f3-l gray mt0 lh-copy measure">
7 7
       Interact with the open Web with your authentic self.
8
-    </h3>
8
+    </p>
9 9
     <%= live_render(@conn, Fortress.Web.HomepageAuthLiveView, container: {:div, class: "form--homepageLogin flex-auto w-100"}) %>
10 10
   </div>
11 11
 </main>
12
+<main class="w-100 bg-near-black near-white pa2">
13
+  <div class="flex flex-column items-start justify-start mw8 center pv3">
14
+    <h1 class="f3 measure fw1 light-gray lh-title">What is Fortress?</h1>
15
+    <p class="lh-copy f4 measure">
16
+      <strong>Fortress</strong> provides Web sign-in to services and one's site using their domain name.
17
+    </p>
18
+    <p class="lh-copy f5 measure-wide">
19
+      Using <a class="link light-blue" href="https://indieauth.spec.indieweb.org">IndieAuth</a>, a site can allow
20
+      people to log in using identities that they already own. This makes it easier for you to protect things
21
+      you want and share it only with people you expect to without them having to create a new account.
22
+    </p>
23
+    <p class="lh-copy f5 measure-wide">
24
+      Currently, Fortress allows people to verify their identities using the following:
25
+    </p>
26
+    <ul>
27
+      <li class="lh-copy">GitHub</li>
28
+      <li class="lh-copy">GitLab</li>
29
+    </ul>
30
+  </div>
31
+</main>

+ 2
- 2
web/templates/resolver/present.html.leex View File

@@ -1,9 +1,9 @@
1 1
 <main class="w-100 bg-near-white near-black pa2" phx-hook="Generic">
2 2
   <div class="flex flex-column items-start justify-start mw7 center pv3">
3
-    <h3 class="f3 f4-l measure fw1 sans dark-gray tracked-tight lh-title ttu">
3
+    <h3 class="f3 f2-l measure fw1 sans dark-gray tracked-tight lh-title small-caps">
4 4
       Providers
5 5
     </h3>
6
-    <p class="lh-copy f5">
6
+    <p class="lh-copy measure f5">
7 7
       Pick a method of logging in that you'd like to use.
8 8
     </p>
9 9
     <div class="flex flex-column mw5 w-100">

+ 3
- 0
web/views/oauth2.ex View File

@@ -0,0 +1,3 @@
1
+defmodule Fortress.Web.OAuth2View do
2
+  use Fortress.Web, :view
3
+end

Loading…
Cancel
Save