Code Monkey home page Code Monkey logo

exbankid's Introduction

exBankID

license: MIT test Coverage Status hex version

Introduction

ExBankID is a simple stateless elixir client for the Swedish BankID API.

Installation

This library is available as a package on hex.pm and can be installed by adding {:ex_bank_id, "~> 0.2.2", hex: :exBankID} to your list of dependencies in mix.exs.

Optional dependencies:

This library depends on an implementation of ExBankID.Http.Client and ExBankID.Json.Handler to be available. If no custom implementations are declared in the config or in the opts passed to the functions in ExBankID the default implementations will be used. For the defaults to work the add this to your list of dependencies in mix.exs

{:poison, "~> 4.0"} # Add this to your deps if you want to use the default json handler
{:httpoison, "~> 1.7"} # Add this to your deps if you want to use the default http client

Configuration

# config/config.exs

config :ex_bank_id,
  # Using a custom http client. Should be a module that implements ExBankID.Http.Client.
  # Defaults to ExBankID.Http.Default
  http_client: MyApp.Http.Client

  # Using a custom json handler. Should be a module that implements ExBankID.Json.Handler.
  # Defaults to ExBankID.Json.Default
  json_handler: MyApp.Json.Handler

  # The path to the client cert file used to authenticate with the BankID API
  # Defaults to the test cert in the assets directory.
  cert_file: "/path/to/cert/file.pem"

  # BankID API url
  # Defaults to "https://appapi2.test.bankid.com/rp/v5.1/"
  url: "https://appapi2.bankid.com/rp/<api version 5 or 5.1>/"

All the above configuration options can be overridden by setting the new value for the corresponding key in the opts Keyword list passed to any of the functions in ExBankID.

Example:

# This will override the url in the config.
ExBankID.auth("1.1.1.1", url: "my.mock-server.local")

# This will override the configured/default json handler and url
ExBankID.auth("1.1.1.1", json_handler: Custom.Json.Handler, url: "my.mock-server.local")

Basic usage

# Authenticate with ip address and optionally the personal number (12 digits)
iex> {:ok, authentication} = ExBankID.auth("1.1.1.1", personal_number: "190000000000")
{:ok,
 %ExBankID.Auth.Response{
   autoStartToken: "3241031e-d849-4e3a-a662-1a36e65eff93",
   orderRef: "9b69419c-b3ac-4f7c-9796-bf54f1a4e40b",
   qrStartSecret: "c0846df5-f96d-49c0-9ef5-4126cd9376e9",
   qrStartToken: "3fb97679-98cb-42da-afe6-62aecbaaab7e"
 }}

# Collect the status of the initiated authentication either with the orderRef
# or with the ExBankID.Auth.Response struct
iex> {:ok, collect_response} = ExBankID.collect("9b69419c-b3ac-4f7c-9796-bf54f1a4e40b")
{:ok,
 %ExBankID.Collect.Response{
   completionData: %ExBankID.Collect.CompletionData{
     cert: %{},
     device: %{},
     ocspResponse: nil,
     signature: nil,
     user: %ExBankID.Collect.User{
       givenName: nil,
       name: nil,
       personalNumber: nil,
       surname: nil
     }
   },
   hintCode: "outstandingTransaction",
   orderRef: "1fadf49f-c695-4bb3-869a-61aee9678009",
   status: "pending"
 }}

 # Using ExBankID.Auth.Response struct
 iex> {:ok, collect_response} = ExBankID.collect(authentication)
{:ok,
 %ExBankID.Collect.Response{
   completionData: %ExBankID.Collect.CompletionData{
     cert: %{},
     device: %{},
     ocspResponse: nil,
     signature: nil,
     user: %ExBankID.Collect.User{
       givenName: nil,
       name: nil,
       personalNumber: nil,
       surname: nil
     }
   },
   hintCode: "outstandingTransaction",
   orderRef: "1fadf49f-c695-4bb3-869a-61aee9678009",
   status: "pending"
 }}

 # When authentication is completed by the end user the fields in CompletionData will
 # be populated.

 #User signing a given message.
 iex> {:ok, sign} = ExBankID.sign(
                "1.1.1.1",
                "This will be displayed in the BankID app",
                personal_number: "190000000000",    # Optional
                user_non_visible_data: "Not displayed" # Optional
                )
{:ok,
 %ExBankID.Sign.Response{
   autoStartToken: "c7b67410-c376-4d27-9aff-f7e331082619",
   orderRef: "90b3816d-c1d3-4650-aa4d-26d9996160de",
   qrStartSecret: "f28787ec-a554-4db4-90c6-dd662dd249bc",
   qrStartToken: "c7a2373b-9a7a-470f-816f-0af0c3d82053"
 }}
# Collecting is done the same way as for a authentication.

# Canceling a sign or authentication
iex> {:ok, _} = ExBankID.cancel(authentication)
{:ok, %{}}

exbankid's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

exbankid's Issues

Pluggable JSON-encoder / decoder

Add the possibility to change what JSON-encoder/decoder is used.

  • Implementation should be similar to that of the HTTP client
  • Configuration option controlling what encode/decoder to use should be added
  • Configuration must be overridable in the opts Keyword list passed to the functions in ExBankID
  • A default encoder/decoder should be implemented and used as the default
  • Any dependencies specific to this issue should be soft dependencies. (Marked as optional)

Support for animated QR code

Add support for generating animated QR-codes. 4.2

  • Add timestamp to authentication and sign responses to keep track of "the number of seconds since the response from auth or sign was returned"
  • For now, only generate the binary needed to create the QR-code.

Dependency ssl_verify_fun (v1.1.6) breaks compilation on Elixir 1.15

Environment

Erlang, Elixir and OTP versions

Erlang/OTP 26 [erts-14.1.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit]

Elixir 1.15.7 (compiled with Erlang/OTP 26)

Operating system

macOS Sonoma 14.1

Current behaviour

Compilation of dependency :ssl_verify_fun with Elixir 1.15 fails.

To reproduce

git clone https://github.com/anfly0/exBankID.git
cd exBankID
mix deps.get
mix compile

Output

==> ssl_verify_fun
Compiling 7 files (.erl)
src/ssl_verify_fingerprint.erl:15:14: can't find include lib "public_key/include/public_key.hrl"
%   15| -include_lib("public_key/include/public_key.hrl").
%     |              ^

src/ssl_verify_fun_cert_helpers.erl:13:14: can't find include lib "public_key/include/public_key.hrl"
%   13| -include_lib("public_key/include/public_key.hrl").
%     |              ^

src/ssl_verify_pk.erl:14:14: can't find include lib "public_key/include/public_key.hrl"
%   14| -include_lib("public_key/include/public_key.hrl").
%     |              ^

src/ssl_verify_hostname.erl:16:14: can't find include lib "public_key/include/public_key.hrl"
%   16| -include_lib("public_key/include/public_key.hrl").
%     |              ^

src/ssl_verify_fingerprint.erl:27:26: record 'OTPCertificate' undefined
%   27| -spec verify_fun(Cert :: #'OTPCertificate'{},
%     |                          ^

src/ssl_verify_fun_cert_helpers.erl:23:34: undefined macro 'id-ce-subjectAltName'
%   23|   AltSubject = select_extension(?'id-ce-subjectAltName', Extensions),
%     |                                  ^

src/ssl_verify_pk.erl:26:26: record 'OTPCertificate' undefined
%   26| -spec verify_fun(Cert :: #'OTPCertificate'{},
%     |                          ^

src/ssl_verify_hostname.erl:28:26: record 'OTPCertificate' undefined
%   28| -spec verify_fun(Cert :: #'OTPCertificate'{},
%     |                          ^

src/ssl_verify_fingerprint.erl:29:39: record 'Extension' undefined
%   29|                           {extension, #'Extension'{}}, InitialUserState :: term()) ->
%     |                                       ^

src/ssl_verify_fun_cert_helpers.erl:9:2: function extract_dns_names/1 undefined
%    9| -export([extract_dns_names/1,
%     |  ^

src/ssl_verify_pk.erl:28:39: record 'Extension' undefined
%   28|                           {extension, #'Extension'{}}, InitialUserState :: term()) ->
%     |                                       ^

src/ssl_verify_hostname.erl:30:39: record 'Extension' undefined
%   30|                           {extension, #'Extension'{}}, InitialUserState :: term()) ->
%     |                                       ^

src/ssl_verify_fingerprint.erl:52:39: record 'OTPCertificate' undefined
%   52| -spec verify_cert_fingerprint(Cert :: #'OTPCertificate'{}, Fingerprint :: fingerprint()) ->
%     |                                       ^

src/ssl_verify_fun_cert_helpers.erl:19:2: spec for undefined function extract_dns_names/1
%   19| -spec extract_dns_names(Cert :: #'OTPCertificate'{}) -> [] | [string()].
%     |  ^

src/ssl_verify_pk.erl:51:30: record 'OTPCertificate' undefined
%   51| -spec verify_cert_pk(Cert :: #'OTPCertificate'{}, Pk :: pk()) ->
%     |                              ^

src/ssl_verify_hostname.erl:46:36: record 'OTPCertificate' undefined
%   46| -spec verify_cert_hostname(Cert :: #'OTPCertificate'{}, Hostname :: hostname()) ->
%     |                                    ^

src/ssl_verify_fun_cert_helpers.erl:19:33: record 'OTPCertificate' undefined
%   19| -spec extract_dns_names(Cert :: #'OTPCertificate'{}) -> [] | [string()].
%     |                                 ^

src/ssl_verify_hostname.erl:76:38: record 'OTPCertificate' undefined
%   76|                              Cert :: #'OTPCertificate'{},
%     |                                      ^

src/ssl_verify_fun_cert_helpers.erl:32:26: record 'OTPCertificate' undefined
%   32| -spec extract_cn(Cert :: #'OTPCertificate'{}) -> {error, no_common_name} | {ok, string()} | {error, invalid}.
%     |                          ^

src/ssl_verify_fun_cert_helpers.erl:34:17: record 'OTPCertificate' undefined
%   34|   TBSCert = Cert#'OTPCertificate'.tbsCertificate,
%     |                 ^

src/ssl_verify_fun_cert_helpers.erl:35:32: record 'OTPTBSCertificate' undefined
%   35|   {rdnSequence, List} = TBSCert#'OTPTBSCertificate'.subject,
%     |                                ^

src/ssl_verify_fun_cert_helpers.erl:38:26: record 'OTPCertificate' undefined
%   38| -spec extract_pk(Cert :: #'OTPCertificate'{}) -> {error, no_common_name} | #'SubjectPublicKeyInfo'{}.
%     |                          ^

src/ssl_verify_fun_cert_helpers.erl:38:76: record 'SubjectPublicKeyInfo' undefined
%   38| -spec extract_pk(Cert :: #'OTPCertificate'{}) -> {error, no_common_name} | #'SubjectPublicKeyInfo'{}.
%     |                                                                            ^

src/ssl_verify_fun_cert_helpers.erl:40:17: record 'OTPCertificate' undefined
%   40|   TBSCert = Cert#'OTPCertificate'.tbsCertificate,
%     |                 ^

src/ssl_verify_fun_cert_helpers.erl:41:26: record 'OTPTBSCertificate' undefined
%   41|   PublicKeyInfo = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
%     |                          ^

src/ssl_verify_fun_cert_helpers.erl:42:16: record 'OTPSubjectPublicKeyInfo' undefined
%   42|   PublicKeyInfo#'OTPSubjectPublicKeyInfo'.subjectPublicKey.
%     |                ^

src/ssl_verify_fun_cert_helpers.erl:48:24: record 'Extension' undefined
%   48| -spec extensions_list([#'Extension'{}] | asn1_NOVALUE) -> [] | [#'Extension'{}].
%     |                        ^

src/ssl_verify_fun_cert_helpers.erl:48:65: record 'Extension' undefined
%   48| -spec extensions_list([#'Extension'{}] | asn1_NOVALUE) -> [] | [#'Extension'{}].
%     |                                                                 ^

src/ssl_verify_fun_cert_helpers.erl:55:39: record 'Extension' undefined
%   55| -spec select_extension(Id :: term(), [#'Extension'{}]) -> undefined | #'Extension'{}.
%     |                                       ^

src/ssl_verify_fun_cert_helpers.erl:55:71: record 'Extension' undefined
%   55| -spec select_extension(Id :: term(), [#'Extension'{}]) -> undefined | #'Extension'{}.
%     |                                                                       ^

src/ssl_verify_fun_cert_helpers.erl:57:28: record 'Extension' undefined
%   57|   Matching = [Extension || #'Extension'{extnID = ExtId} = Extension <- Extensions, ExtId =:= Id],
%     |                            ^

src/ssl_verify_fun_cert_helpers.erl:57:84: variable 'ExtId' is unbound
%   57|   Matching = [Extension || #'Extension'{extnID = ExtId} = Extension <- Extensions, ExtId =:= Id],
%     |                                                                                    ^

src/ssl_verify_fun_cert_helpers.erl:75:15: record 'AttributeTypeAndValue' undefined
%   75| extract_cn2([[#'AttributeTypeAndValue'{type={2, 5, 4, 3},
%     |               ^

src/ssl_verify_fun_cert_helpers.erl:77:39: variable 'CN' is unbound
%   77|   ssl_verify_fun_encodings:get_string(CN);
%     |                                       ^

src/ssl_verify_fun_cert_helpers.erl:49:1: Warning: function extensions_list/1 is unused
%   49| extensions_list(E) ->
%     | ^

src/ssl_verify_fun_cert_helpers.erl:56:1: Warning: function select_extension/2 is unused
%   56| select_extension(Id, Extensions) ->
%     | ^

src/ssl_verify_fun_cert_helpers.erl:64:1: Warning: function extract_dns_names_from_alt_names/2 is unused
%   64| extract_dns_names_from_alt_names([ExtValue | Rest], Acc) ->
%     | ^

could not compile dependency :ssl_verify_fun, "mix compile" failed. Errors may have been logged above. You can recompile this dependency with "mix deps.compile ssl_verify_fun --force", update it with "mix deps.update ssl_verify_fun" or clean it with "mix deps.clean ssl_verify_fun"

Expected behaviour

To compile as with Elixir 1.14.

Environment

Erlang/OTP 26 [erts-14.1.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit]

Elixir 1.14.5 (compiled with Erlang/OTP 26)

To reproduce

git clone https://github.com/anfly0/exBankID.git
cd exBankID
mix deps.get
mix compile

Output

[intentionally omitted]

Generated uuid app
==> ex_bank_id
Compiling 23 files (.ex)
Generated ex_bank_id app

Troubleshooting

The package :ssl_fun_verify is a dependency of :hackney which is a dependency of :httpoison.

It seems that version 1.1.6 of :ssl_fun_verify suffered from an incorrect mix file. One could override that dependency and specify :rebar3 as a manager.

However, the incorrect mix file was fixed in version 1.1.7 of :ssl_verify_fun.

Upgrading :httpoison to ~> 1.8 resolves the issue.

Running credo fails

Environment

Erlang, Elixir and OTP versions

Erlang/OTP 26 [erts-14.1.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit]

Elixir 1.15.7 (compiled with Erlang/OTP 26)

Operating system

macOS Sonoma 14.1

Current behaviour

Running mix credo fails throwing errors.

To reproduce

git clone https://github.com/anfly0/exBankID.git
cd exBankID
mix deps.get
mix compile
mix credo

Output

[intentionally omitted]

07:59:09.041 [error] Task #PID<0.4062.0> started from #PID<0.4030.0> terminating
** (CaseClauseError) no case clause matching: {:ok, 4, 1, [], [{:alias, {1, 1, ~c"ExUnit"}, :ExUnit}, {:., {1, 7, nil}}, {:paren_identifier, {1, 8, ~c"start"}, :start}, {:"(", {1, 13, nil}}, {:")", {1, 14, nil}}, {:eol, {1, 15, 2}}, {:alias, {3, 1, ~c"Code"}, :Code}, {:., {3, 5, nil}}, {:paren_identifier, {3, 6, ~c"require_file"}, :require_file}, {:"(", {3, 18, nil}}, {:bin_string, {3, 19, nil}, ["./test/helpers/helpers_test.exs"]}, {:")", {3, 52, nil}}, {:eol, {3, 53, 1}}]}
    (credo 1.5.3) lib/credo/code.ex:136: Credo.Code.to_tokens/2
    (credo 1.5.3) lib/credo/check/readability/parentheses_in_condition.ex:35: Credo.Check.Readability.ParenthesesInCondition.run/2
    (credo 1.5.3) lib/credo/check/readability/parentheses_in_condition.ex:2: Credo.Check.Readability.ParenthesesInCondition.do_run_on_source_file/3
    (elixir 1.15.7) lib/task/supervised.ex:101: Task.Supervised.invoke_mfa/2
    (elixir 1.15.7) lib/task/supervised.ex:36: Task.Supervised.reply/4
Function: #Function<5.56087938/0 in Credo.Check.Readability.ParenthesesInCondition.do_run_on_all_source_files/3>
    Args: []

Expected behaviour

Successful execution of mix credo with resulting static analysis.

Troubleshooting

I haven't dug deep into this but experienced the same issue on Elixir 1.14 and 1.13.

Upgrading credo to at least 1.6.0 resolves the issue.

Pem file inline instead of file.

Is it possible have the pem file inlined in an env file instead?
Have a multi-server setup so would be nice not to have to be forced to have multiple instances of the file.

Support for requirement parameter

Add support for the requirement parameter 14.5

  • Should be added to the Authentication and Sign payload structs.
  • Convenience methods constructing a valid "requirements map".

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.