Initial commit
This commit is contained in:
commit
941652e0f4
59
.gitignore
vendored
Normal file
59
.gitignore
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
|
||||||
|
# Created by https://www.gitignore.io/api/elixir,osx,vim
|
||||||
|
|
||||||
|
### Elixir ###
|
||||||
|
/_build
|
||||||
|
/cover
|
||||||
|
/deps
|
||||||
|
erl_crash.dump
|
||||||
|
*.ez
|
||||||
|
*.beam
|
||||||
|
|
||||||
|
### Elixir Patch ###
|
||||||
|
/doc
|
||||||
|
### OSX ###
|
||||||
|
*.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
### Vim ###
|
||||||
|
# swap
|
||||||
|
[._]*.s[a-v][a-z]
|
||||||
|
[._]*.sw[a-p]
|
||||||
|
[._]s[a-v][a-z]
|
||||||
|
[._]sw[a-p]
|
||||||
|
# session
|
||||||
|
Session.vim
|
||||||
|
# temporary
|
||||||
|
.netrwhist
|
||||||
|
*~
|
||||||
|
# auto-generated tag files
|
||||||
|
tags
|
||||||
|
|
||||||
|
# End of https://www.gitignore.io/api/elixir,osx,vim
|
||||||
|
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.elixir_ls
|
22
.travis.yml
Normal file
22
.travis.yml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
language: elixir
|
||||||
|
|
||||||
|
elixir:
|
||||||
|
- 1.5
|
||||||
|
- 1.4
|
||||||
|
|
||||||
|
otp_release:
|
||||||
|
- 20.0
|
||||||
|
- 19.3
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- MIX_ENV=test
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email: false
|
||||||
|
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
script:
|
||||||
|
- mix test
|
||||||
|
- mix credo
|
3
README.md
Normal file
3
README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Überauth IcyNet
|
||||||
|
|
||||||
|
> Icy Network OAuth2 strategy for Überauth.
|
30
config/config.exs
Normal file
30
config/config.exs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# This file is responsible for configuring your application
|
||||||
|
# and its dependencies with the aid of the Mix.Config module.
|
||||||
|
use Mix.Config
|
||||||
|
|
||||||
|
# This configuration is loaded before any dependency and is restricted
|
||||||
|
# to this project. If another project depends on this project, this
|
||||||
|
# file won't be loaded nor affect the parent project. For this reason,
|
||||||
|
# if you want to provide default values for your application for
|
||||||
|
# 3rd-party users, it should be done in your "mix.exs" file.
|
||||||
|
|
||||||
|
# You can configure for your application as:
|
||||||
|
#
|
||||||
|
# config :ueberauth_icynet, key: :value
|
||||||
|
#
|
||||||
|
# And access this configuration in your application as:
|
||||||
|
#
|
||||||
|
# Application.get_env(:ueberauth_icynet, :key)
|
||||||
|
#
|
||||||
|
# Or configure a 3rd-party app:
|
||||||
|
#
|
||||||
|
# config :logger, level: :info
|
||||||
|
#
|
||||||
|
|
||||||
|
# It is also possible to import configuration files, relative to this
|
||||||
|
# directory. For example, you can emulate configuration per environment
|
||||||
|
# by uncommenting the line below and defining dev.exs, test.exs and such.
|
||||||
|
# Configuration from the imported file will override the ones defined
|
||||||
|
# here (which is why it is important to import them last).
|
||||||
|
#
|
||||||
|
# import_config "#{Mix.env}.exs"
|
3
lib/ueber_icynet.ex
Normal file
3
lib/ueber_icynet.ex
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
defmodule UeberauthIcyNet do
|
||||||
|
@moduledoc false
|
||||||
|
end
|
202
lib/ueberauth/strategy/icynet.ex
Normal file
202
lib/ueberauth/strategy/icynet.ex
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
defmodule Ueberauth.Strategy.IcyNet do
|
||||||
|
@moduledoc """
|
||||||
|
Provides an Ueberauth strategy for authenticating with Icy Network.
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
Obtain a Client ID and Secret from Icy Network.
|
||||||
|
|
||||||
|
Include the provider in your configuration for Ueberauth
|
||||||
|
|
||||||
|
config :ueberauth, Ueberauth,
|
||||||
|
providers: [
|
||||||
|
icynet: { Ueberauth.Strategy.IcyNet, [] }
|
||||||
|
]
|
||||||
|
|
||||||
|
Then include the configuration for icynet.
|
||||||
|
|
||||||
|
config :ueberauth, Ueberauth.Strategy.IcyNet.OAuth,
|
||||||
|
client_id: System.get_env("ICYNET_CLIENT_ID"),
|
||||||
|
client_secret: System.get_env("ICYNET_CLIENT_SECRET")
|
||||||
|
|
||||||
|
If you haven't already, create a pipeline and setup routes for your callback handler
|
||||||
|
|
||||||
|
pipeline :auth do
|
||||||
|
Ueberauth.plug "/auth"
|
||||||
|
end
|
||||||
|
|
||||||
|
scope "/auth" do
|
||||||
|
pipe_through [:browser, :auth]
|
||||||
|
|
||||||
|
get "/:provider/callback", AuthController, :callback
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
Create an endpoint for the callback where you will handle the `Ueberauth.Auth` struct
|
||||||
|
|
||||||
|
defmodule MyApp.AuthController do
|
||||||
|
use MyApp.Web, :controller
|
||||||
|
|
||||||
|
def callback_phase(%{ assigns: %{ ueberauth_failure: fails } } = conn, _params) do
|
||||||
|
# do things with the failure
|
||||||
|
end
|
||||||
|
|
||||||
|
def callback_phase(%{ assigns: %{ ueberauth_auth: auth } } = conn, params) do
|
||||||
|
# do things with the auth
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
You can edit the behaviour of the Strategy by including some options when you register your provider.
|
||||||
|
|
||||||
|
To set the `uid_field`
|
||||||
|
|
||||||
|
config :ueberauth, Ueberauth,
|
||||||
|
providers: [
|
||||||
|
icynet: { Ueberauth.Strategy.IcyNet, [uid_field: :email] }
|
||||||
|
]
|
||||||
|
|
||||||
|
Default is `:id`
|
||||||
|
|
||||||
|
To set the default 'scopes' (permissions):
|
||||||
|
|
||||||
|
config :ueberauth, Ueberauth,
|
||||||
|
providers: [
|
||||||
|
icynet: { Ueberauth.Strategy.IcyNet, [default_scope: "email,image"] }
|
||||||
|
]
|
||||||
|
|
||||||
|
Default is empty ("") which "Grants read-only access to public information (includes public user profile info, public repository info, and gists)"
|
||||||
|
"""
|
||||||
|
use Ueberauth.Strategy,
|
||||||
|
uid_field: :id,
|
||||||
|
default_scope: "",
|
||||||
|
oauth2_module: Ueberauth.Strategy.IcyNet.OAuth
|
||||||
|
|
||||||
|
alias Ueberauth.Auth.Info
|
||||||
|
alias Ueberauth.Auth.Credentials
|
||||||
|
alias Ueberauth.Auth.Extra
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Handles the initial redirect to the icynet authentication page.
|
||||||
|
|
||||||
|
To customize the scope (permissions) that are requested by icynet include them as part of your url:
|
||||||
|
|
||||||
|
"/auth/icynet?scope=email,image,privilege"
|
||||||
|
|
||||||
|
You can also include a `state` param that icynet will return to you.
|
||||||
|
"""
|
||||||
|
def handle_request!(conn) do
|
||||||
|
scopes = conn.params["scope"] || option(conn, :default_scope)
|
||||||
|
send_redirect_uri = Keyword.get(options(conn), :send_redirect_uri, true)
|
||||||
|
|
||||||
|
opts =
|
||||||
|
if send_redirect_uri do
|
||||||
|
[redirect_uri: callback_url(conn), scope: scopes]
|
||||||
|
else
|
||||||
|
[scope: scopes]
|
||||||
|
end
|
||||||
|
|
||||||
|
opts =
|
||||||
|
if conn.params["state"], do: Keyword.put(opts, :state, conn.params["state"]), else: opts
|
||||||
|
|
||||||
|
module = option(conn, :oauth2_module)
|
||||||
|
redirect!(conn, apply(module, :authorize_url!, [opts]))
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Handles the callback from IcyNet. When there is a failure from IcyNet the failure is included in the
|
||||||
|
`ueberauth_failure` struct. Otherwise the information returned from IcyNet is returned in the `Ueberauth.Auth` struct.
|
||||||
|
"""
|
||||||
|
def handle_callback!(%Plug.Conn{params: %{"code" => code}} = conn) do
|
||||||
|
module = option(conn, :oauth2_module)
|
||||||
|
token = apply(module, :get_token!, [[code: code]])
|
||||||
|
|
||||||
|
if token.access_token == nil do
|
||||||
|
set_errors!(conn, [
|
||||||
|
error(token.other_params["error"], token.other_params["error_description"])
|
||||||
|
])
|
||||||
|
else
|
||||||
|
fetch_user(conn, token)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def handle_callback!(conn) do
|
||||||
|
set_errors!(conn, [error("missing_code", "No code received")])
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Cleans up the private area of the connection used for passing the raw IcyNet response around during the callback.
|
||||||
|
"""
|
||||||
|
def handle_cleanup!(conn) do
|
||||||
|
conn
|
||||||
|
|> put_private(:icynet_user, nil)
|
||||||
|
|> put_private(:icynet_token, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Fetches the uid field from the IcyNet response. This defaults to the option `uid_field` which in-turn defaults to `id`
|
||||||
|
"""
|
||||||
|
def uid(conn) do
|
||||||
|
conn |> option(:uid_field) |> to_string()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Includes the credentials from the IcyNet response.
|
||||||
|
"""
|
||||||
|
def credentials(conn) do
|
||||||
|
token = conn.private.icynet_token
|
||||||
|
|
||||||
|
%Credentials{
|
||||||
|
token: token.access_token,
|
||||||
|
refresh_token: token.refresh_token,
|
||||||
|
expires_at: token.expires_at,
|
||||||
|
token_type: token.token_type,
|
||||||
|
expires: !!token.expires_in
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Fetches the fields to populate the info section of the `Ueberauth.Auth` struct.
|
||||||
|
"""
|
||||||
|
def info(conn) do
|
||||||
|
user = conn.private.icynet_user
|
||||||
|
|
||||||
|
%Info{
|
||||||
|
name: user["username"],
|
||||||
|
nickname: user["display_name"],
|
||||||
|
email: user["email"],
|
||||||
|
image: user["image"],
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Stores the raw information (including the token) obtained from the IcyNet callback.
|
||||||
|
"""
|
||||||
|
def extra(conn) do
|
||||||
|
%Extra{
|
||||||
|
raw_info: %{
|
||||||
|
token: conn.private.icynet_token,
|
||||||
|
user: conn.private.icynet_user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fetch_user(conn, token) do
|
||||||
|
conn = put_private(conn, :icynet_token, token)
|
||||||
|
case Ueberauth.Strategy.IcyNet.OAuth.get(token, "/oauth2/user") do
|
||||||
|
{:ok, %OAuth2.Response{status_code: 401, body: _body}} ->
|
||||||
|
set_errors!(conn, [error("token", "unauthorized")])
|
||||||
|
|
||||||
|
{:ok, %OAuth2.Response{status_code: status_code, body: user}}
|
||||||
|
when status_code in 200..399 ->
|
||||||
|
put_private(conn, :icynet_user, user)
|
||||||
|
|
||||||
|
{:error, %OAuth2.Error{reason: reason}} ->
|
||||||
|
set_errors!(conn, [error("OAuth2", reason)])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp option(conn, key) do
|
||||||
|
Keyword.get(options(conn), key, Keyword.get(default_options(), key))
|
||||||
|
end
|
||||||
|
end
|
117
lib/ueberauth/strategy/icynet/oauth.ex
Normal file
117
lib/ueberauth/strategy/icynet/oauth.ex
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
defmodule Ueberauth.Strategy.IcyNet.OAuth do
|
||||||
|
@moduledoc """
|
||||||
|
An implementation of OAuth2 for icynet.
|
||||||
|
|
||||||
|
To add your `client_id` and `client_secret` include these values in your configuration.
|
||||||
|
|
||||||
|
config :ueberauth, Ueberauth.Strategy.IcyNet.OAuth,
|
||||||
|
client_id: System.get_env("ICYNET_CLIENT_ID"),
|
||||||
|
client_secret: System.get_env("ICYNET_CLIENT_SECRET")
|
||||||
|
"""
|
||||||
|
use OAuth2.Strategy
|
||||||
|
|
||||||
|
@defaults [
|
||||||
|
strategy: __MODULE__,
|
||||||
|
site: "https://icynet.eu",
|
||||||
|
authorize_url: "https://icynet.eu/oauth2/authorize",
|
||||||
|
token_url: "https://icynet.eu/oauth2/token",
|
||||||
|
token_method: :post
|
||||||
|
]
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Construct a client for requests to IcyNet.
|
||||||
|
|
||||||
|
Optionally include any OAuth2 options here to be merged with the defaults.
|
||||||
|
|
||||||
|
Ueberauth.Strategy.IcyNet.OAuth.client(redirect_uri: "http://localhost:4000/auth/icynet/callback")
|
||||||
|
|
||||||
|
This will be setup automatically for you in `Ueberauth.Strategy.IcyNet`.
|
||||||
|
These options are only useful for usage outside the normal callback phase of Ueberauth.
|
||||||
|
"""
|
||||||
|
def client(opts \\ []) do
|
||||||
|
config =
|
||||||
|
:ueberauth
|
||||||
|
|> Application.fetch_env!(Ueberauth.Strategy.IcyNet.OAuth)
|
||||||
|
|> check_credential(:client_id)
|
||||||
|
|> check_credential(:client_secret)
|
||||||
|
|
||||||
|
client_opts =
|
||||||
|
@defaults
|
||||||
|
|> Keyword.merge(config)
|
||||||
|
|> Keyword.merge(opts)
|
||||||
|
|
||||||
|
json_library = Ueberauth.json_library()
|
||||||
|
|
||||||
|
OAuth2.Client.new(client_opts)
|
||||||
|
|> OAuth2.Client.put_serializer("application/json", json_library)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Provides the authorize url for the request phase of Ueberauth. No need to call this usually.
|
||||||
|
"""
|
||||||
|
def authorize_url!(params \\ [], opts \\ []) do
|
||||||
|
opts
|
||||||
|
|> client
|
||||||
|
|> OAuth2.Client.authorize_url!(params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get(token, url, headers \\ [], opts \\ []) do
|
||||||
|
[token: token]
|
||||||
|
|> client
|
||||||
|
|> put_param("access_token", token)
|
||||||
|
|> OAuth2.Client.get(url, headers, opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_token!(params \\ [], options \\ []) do
|
||||||
|
headers = Keyword.get(options, :headers, [])
|
||||||
|
options = Keyword.get(options, :options, [])
|
||||||
|
client_options = Keyword.get(options, :client_options, [])
|
||||||
|
client = OAuth2.Client.get_token!(client(client_options), params, headers, options)
|
||||||
|
client.token
|
||||||
|
end
|
||||||
|
|
||||||
|
# Strategy Callbacks
|
||||||
|
|
||||||
|
def authorize_url(client, params) do
|
||||||
|
client
|
||||||
|
|> put_param("response_type", "code")
|
||||||
|
|> put_param("redirect_uri", client().redirect_uri)
|
||||||
|
OAuth2.Strategy.AuthCode.authorize_url(client, params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_token(client, params, headers) do
|
||||||
|
client
|
||||||
|
|> put_param("client_id", client.client_id)
|
||||||
|
|> put_param("client_secret", client.client_secret)
|
||||||
|
|> put_param("grant_type", "authorization_code")
|
||||||
|
|> put_param("redirect_uri", client().redirect_uri)
|
||||||
|
|> put_header("Accept", "application/json")
|
||||||
|
|> OAuth2.Strategy.AuthCode.get_token(params, headers)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp check_credential(config, key) do
|
||||||
|
check_config_key_exists(config, key)
|
||||||
|
|
||||||
|
case Keyword.get(config, key) do
|
||||||
|
value when is_binary(value) ->
|
||||||
|
config
|
||||||
|
{:system, env_key} ->
|
||||||
|
case System.get_env(env_key) do
|
||||||
|
nil ->
|
||||||
|
raise "#{inspect (env_key)} missing from environment, expected in config :ueberauth, Ueberauth.Strategy.IcyNet"
|
||||||
|
value ->
|
||||||
|
Keyword.put(config, key, value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp check_config_key_exists(config, key) when is_list(config) do
|
||||||
|
unless Keyword.has_key?(config, key) do
|
||||||
|
raise "#{inspect (key)} missing from config :ueberauth, Ueberauth.Strategy.IcyNet"
|
||||||
|
end
|
||||||
|
config
|
||||||
|
end
|
||||||
|
defp check_config_key_exists(_, _) do
|
||||||
|
raise "Config :ueberauth, Ueberauth.Strategy.IcyNet is not a keyword list, as expected"
|
||||||
|
end
|
||||||
|
end
|
52
mix.exs
Normal file
52
mix.exs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
defmodule Ueberauth.IcyNet.Mixfile do
|
||||||
|
use Mix.Project
|
||||||
|
|
||||||
|
@version "0.0.1"
|
||||||
|
|
||||||
|
def project do
|
||||||
|
[app: :ueberauth_icynet,
|
||||||
|
version: @version,
|
||||||
|
name: "Ueberauth IcyNet",
|
||||||
|
package: package(),
|
||||||
|
elixir: "~> 1.3",
|
||||||
|
build_embedded: Mix.env == :prod,
|
||||||
|
start_permanent: Mix.env == :prod,
|
||||||
|
source_url: "https://gitlab.icynet.eu/IcyNetwork/ueberauth_icynet",
|
||||||
|
homepage_url: "https://gitlab.icynet.eu/IcyNetwork/ueberauth_icynet",
|
||||||
|
description: description(),
|
||||||
|
deps: deps(),
|
||||||
|
docs: docs()]
|
||||||
|
end
|
||||||
|
|
||||||
|
def application do
|
||||||
|
[applications: [:logger, :ueberauth, :oauth2]]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp deps do
|
||||||
|
[
|
||||||
|
{:oauth2, "~> 1.0 or ~> 2.0"},
|
||||||
|
{:ueberauth, "~> 0.6.0"},
|
||||||
|
|
||||||
|
# dev/test only dependencies
|
||||||
|
{:credo, "~> 0.8", only: [:dev, :test]},
|
||||||
|
|
||||||
|
# docs dependencies
|
||||||
|
{:ex_doc, ">= 0.0.0", only: :dev}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp docs do
|
||||||
|
[extras: ["README.md"]]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp description do
|
||||||
|
"An Ueberauth strategy for using IcyNet to authenticate your users."
|
||||||
|
end
|
||||||
|
|
||||||
|
defp package do
|
||||||
|
[files: ["lib", "mix.exs", "README.md"],
|
||||||
|
maintainers: ["Evert Prants"],
|
||||||
|
licenses: ["CC0"],
|
||||||
|
links: %{"Gitlab": "https://gitlab.icynet.eu/IcyNetwork/ueberauth_icynet"}]
|
||||||
|
end
|
||||||
|
end
|
23
mix.lock
Normal file
23
mix.lock
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
%{
|
||||||
|
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
|
||||||
|
"certifi": {:hex, :certifi, "2.5.2", "b7cfeae9d2ed395695dd8201c57a2d019c0c43ecaf8b8bcb9320b40d6662f340", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "3b3b5f36493004ac3455966991eaf6e768ce9884693d9968055aeeeb1e575040"},
|
||||||
|
"credo": {:hex, :credo, "0.10.2", "03ad3a1eff79a16664ed42fc2975b5e5d0ce243d69318060c626c34720a49512", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "539596b6774069260d5938aa73042a2f5157e1c0215aa35f5a53d83889546d14"},
|
||||||
|
"earmark": {:hex, :earmark, "1.4.4", "4821b8d05cda507189d51f2caeef370cf1e18ca5d7dfb7d31e9cafe6688106a4", [:mix], [], "hexpm", "1f93aba7340574847c0f609da787f0d79efcab51b044bb6e242cae5aca9d264d"},
|
||||||
|
"ex_doc": {:hex, :ex_doc, "0.22.1", "9bb6d51508778193a4ea90fa16eac47f8b67934f33f8271d5e1edec2dc0eee4c", [:mix], [{:earmark, "~> 1.4.0", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "d957de1b75cb9f78d3ee17820733dc4460114d8b1e11f7ee4fd6546e69b1db60"},
|
||||||
|
"hackney": {:hex, :hackney, "1.16.0", "5096ac8e823e3a441477b2d187e30dd3fff1a82991a806b2003845ce72ce2d84", [:rebar3], [{:certifi, "2.5.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.1", [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]}, {:parse_trans, "3.3.0", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.6", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "3bf0bebbd5d3092a3543b783bf065165fa5d3ad4b899b836810e513064134e18"},
|
||||||
|
"idna": {:hex, :idna, "6.0.1", "1d038fb2e7668ce41fbf681d2c45902e52b3cb9e9c77b55334353b222c2ee50c", [:rebar3], [{:unicode_util_compat, "0.5.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a02c8a1c4fd601215bb0b0324c8a6986749f807ce35f25449ec9e69758708122"},
|
||||||
|
"jason": {:hex, :jason, "1.2.1", "12b22825e22f468c02eb3e4b9985f3d0cb8dc40b9bd704730efa11abd2708c44", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b659b8571deedf60f79c5a608e15414085fa141344e2716fbd6988a084b5f993"},
|
||||||
|
"makeup": {:hex, :makeup, "1.0.2", "0b9f7bfb7a88bed961341b359bc2cc1b233517af891ba4890ec5a580ffe738b4", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "43833299231c6a6983afc75a34e43eeba638521d5527ff89809fa6372424fd7e"},
|
||||||
|
"makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"},
|
||||||
|
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
|
||||||
|
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm", "6cbe761d6a0ca5a31a0931bf4c63204bceb64538e664a8ecf784a9a6f3b875f1"},
|
||||||
|
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
|
||||||
|
"nimble_parsec": {:hex, :nimble_parsec, "0.6.0", "32111b3bf39137144abd7ba1cce0914533b2d16ef35e8abc5ec8be6122944263", [:mix], [], "hexpm", "27eac315a94909d4dc68bc07a4a83e06c8379237c5ea528a9acff4ca1c873c52"},
|
||||||
|
"oauth2": {:hex, :oauth2, "2.0.0", "338382079fe16c514420fa218b0903f8ad2d4bfc0ad0c9f988867dfa246731b0", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "881b8364ac7385f9fddc7949379cbe3f7081da37233a1aa7aab844670a91e7e7"},
|
||||||
|
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
|
||||||
|
"plug": {:hex, :plug, "1.10.1", "c56a6d9da7042d581159bcbaef873ba9d87f15dce85420b0d287bca19f40f9bd", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "b5cd52259817eb8a31f2454912ba1cff4990bca7811918878091cb2ab9e52cb8"},
|
||||||
|
"plug_crypto": {:hex, :plug_crypto, "1.1.2", "bdd187572cc26dbd95b87136290425f2b580a116d3fb1f564216918c9730d227", [:mix], [], "hexpm", "6b8b608f895b6ffcfad49c37c7883e8df98ae19c6a28113b02aa1e9c5b22d6b5"},
|
||||||
|
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
|
||||||
|
"ueberauth": {:hex, :ueberauth, "0.6.3", "d42ace28b870e8072cf30e32e385579c57b9cc96ec74fa1f30f30da9c14f3cc0", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "afc293d8a1140d6591b53e3eaf415ca92842cb1d32fad3c450c6f045f7f91b60"},
|
||||||
|
"unicode_util_compat": {:hex, :unicode_util_compat, "0.5.0", "8516502659002cec19e244ebd90d312183064be95025a319a6c7e89f4bccd65b", [:rebar3], [], "hexpm", "d48d002e15f5cc105a696cf2f1bbb3fc72b4b770a184d8420c8db20da2674b38"},
|
||||||
|
}
|
1
test/test_helper.exs
Normal file
1
test/test_helper.exs
Normal file
@ -0,0 +1 @@
|
|||||||
|
ExUnit.start()
|
4
test/ueber_icynet_test.exs
Normal file
4
test/ueber_icynet_test.exs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
defmodule UeberauthIcyNetTest do
|
||||||
|
use ExUnit.Case
|
||||||
|
doctest UeberauthIcyNet
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user