Introduction

This is a Ruby library to interface with the uuOpenIdConnect (uuOIDC) API. It is a part of uuAppServerKit used for development of uuApplications under the Unicorn Application Framework (UAF).

More information about OIDC Flows and internals can be found on OpenIDConnect http://openid.net/connect/

Requirements

This code has been run and tested on:

  • MRI Ruby 2.2.1 on Linux platform - for production usage.
  • MRI Ruby 2.2.1 on Windows 10 platform - for development (not production)
  • JRuby9k may work, but is NOT TESTED, some restrictions are known as unsuported EC-Cryptography

The uu_oidc-server with same or greater version than uu_oidc client expected on server-side for full client functionality/features support.

</a>Installation

This package is available in uuGEMs Repository - https://gems.plus4u.net and can be installed with:

gem install uu_oidcg01 -s https://gems.plus4u.net

For users of bundler:

Gemfile: source 'https://gems.plus4u.net' do gem 'uu_oidcg01', '~>4.2' end
bundler install

API Documentation

The API documentation is provided here: uuOpenIdConnect Client API reference.

Example Usage

The following scenarios shows typical usage of the uuOIDC Client API for users and applications authentication.

  • Client uuOIDC Session login with Resource Owner Password grant flow
  • Client uuOIDC Session login with Authorization Code grant flow
  • Server uuOIDC Session in uuApp Controller
  • Call uuApp Controller with HTTPClient and uuOIDC Session
  • Call uuApp Controller with uuApp Client and uuOIDC Session
  • Call uu8 Command with HTTPClient and uuOIDC Session
  • Call uu8 Command with UU_OS API and uuOIDC Session

The most important class for all scenarios is UuOidc::Session.

Before start

To try samples below some prerequisites are needed:

  • installed uu_oidc gem - see Instalation
  • for uu8 samples you must be a registered plus4u.net user
  • default uuOIDC server provider is https://oidc.plus4u.net/uu-oidcg01-main/0-0
    • for change the provider, use ENV to set default uri
      • Windows: set UU_OIDC_SERVER_URI=https://host:port/uu-oidcg01-main/0-0
      • Linux: export $UU_OIDC_SERVER_URI=https://host:port/uu-oidcg01-main/0-0
    • on testing environment the SSL self-signed certificate could be tolerated, use ENV to disable certificate verification
      • Windows: set UU_OIDC_SSL_VERIFY_MODE=0
      • Linux: export $UU_OIDC_SSL_VERIFY_MODE=0

Client uuOIDC Session login with ResourceOwnerPasswordGrantFlow

This scenario could be used for direct user login - when the application has access to user credentials AccessCode1(username)and AccessCode2(password).
Typical usage is for uuEE authentication. This authentication could be executed on client (console, installed app, …) and server-side (in uuApp Controller), where interaction with user is not possible. Avoid this flow for use cases where the user interaction is possible, than prefer the Interactive Login with Browser scenario.

require 'uu_oidc'


# Change to your user credentials
CREDENTIALS = { access_code1: 'user_frog', access_code2: 'Frog2016Secret$' }

ses=UuOidc::Session.(CREDENTIALS)

puts("Welcome: #{ses.identity}")

The credentials for login could be read from password file, configuration or some other kind of secured storage.
Always externalize the credentials from code, don´t put credentials into codebase (git) repository !!!.

Client uuOIDC Session login with AuthorizationCodeGrantFlow

Following samples shows the best practice for login on client side when user interaction is possible. It uses the most secured flow (AuthorizationCodeGrant), recommended by OpenIDConnect specification. The advantage of this solution lies in possibility to use full power of uuOIDC authentication: * federated login feature - single sign on with Google, Facebook, Microsoft, etc. * UI login page * Remember-Me login * When session already established, user is automatically logged (SingleSignOn with your session in browser) * No need to put, store etc. credentials on client, in code, in passwordfiles, in environment or on other place. Client has no access to your credentials, wow!

The principle of this flow is based on following steps: * Session prepares URI for uuOIDC server call for login * User must open the uuOIDC login URI in browser and follow instructions * when needed, user must sign-in with credentials to uuOIDC - independently on client application * uuOIDC server generates the unique AccessCode * User uses the AccessCode for client Session.login to obtain Session AccessToken from uuOIDC server.

Interactive Login with STD-OUT/-IN

This sample shows all steps for AuthorizationCodeGrantFlow. It could be used in situations, when it is not possible to open Browser from Ruby.

require 'uu_oidc'

uri = UuOidc::Session.get_access_token_grant_code_uri()

puts 'Open following URI in browser and follow instructions to return back here with generated Access Token Code.'
puts "Login URI: #{uri}"
puts 'Enter your Access Token Code:'
access_token_code = STDIN.gets.chomp

ses = UuOidc::Session.(code: access_token_code)
puts "Welcome #{ses.identity[:name]}!"
The better way how to login with interaction with user see the next sample: Interactive Login with Browser ### </a>Interactive Login with Browser This sample simplifies the interaction for user. The browser will be automatically opened with uuOIDC URI and HTTP-Callback server is temporary started for callback from uuOIDC server to accept the generated AccessCode. It is secure, standard solution with smart interaction for user.
require 'uu_oidc'

ses = UuOidc::Session.loginInteractive()
puts "Welcome #{ses.identity[:name]}!"

ses = UuOidc::Session.loginInteractive(prompt: 'login') # force login prompt
puts "Welcome #{ses.identity[:name]}!"

Server uuOIDC Session in uuApp Controller

This chapter shows some typical scenarios used on server-side in uuApp Controllers ### Access Session informations This sample shows access to Session on server side. It is simple uuApp Controller that uses

require 'uu_oidc'

# This OIDCSampleController must be used with uuAppServer
class OIDCSampleController

  def echo(ctrl_env)

    ses = ctrl_env.session() # obtain current oidc session

    dtoOut = {}
    dtoOut[:sid] = ses.sid() # access unique oidc session ID
    dtoOut[:identity] = ses.identity() # obtain info about User identity
    dtoOut[:client_app_identity] = ses.client_app_identity() # obtain info about
    dtoOut[:app_identity] = ses.app_identity() # info about current application
    dtoOut[:call_token] = ses.get_call_token() # generate token to call other apps
    ctrl_env.result = dtoOut
  end

end

Force login from server side

There may be some use cases where the application needs to reassure itself that the authenticated user is already on client side - the login is fresh (max_login_age). Or the application needs to ensure that the authentication was processed with specific secure way - login_level_of_assurance.

require 'uu_oidc'

# This OIDCSampleController must be used with uuAppServer
class OIDCSampleController

  def echo(ctrl_env)

    ses = ctrl_env.session() # obtain current oidc session
    ses.(ctrl_env, { login_level_of_assurance: [UuOidc::Session::OAUTH_ACR_LOA_1, UuOidc::Session::OAUTH_ACR_LOA_2, UuOidc::Session::OAUTH_ACR_LOA_3],
                                 max_login_age: 30 })

    dtoOut = {}
    dtoOut[:sid] = ses.sid() # access unique oidc session ID
    dtoOut[:identity] = ses.identity() # obtain info about User identity
    dtoOut[:client_app_identity] = ses.client_app_identity() # obtain info about
    dtoOut[:app_identity] = ses.app_identity() # info about current application
    dtoOut[:call_token] = ses.get_call_token() # generate token to call other apps
    ctrl_env.result = dtoOut
  end

end

Login as uuEE

You can use Session.login as on client side to login for some specific uuEE and call the remote command from controller as shown in this sample:
The uuEE credentials have to be externalized from source code !!!

require 'uu_oidc'

# This OIDCSampleController must be used with uuAppServer
class OIDCSampleController

  # should be externalized !!! password file, etc.
  UUEE_CREDENTIALS = { access_code1: 'uuee_ac1', access_code2: 'uuee_ac2' }
  CALL_URI = UuOidc::Discovery::Discovery.new().[:userinfo_endpoint]

  def echo(ctrl_env)

    ses=UuOidc::Session.(CREDENTIALS)
    token = ses.get_call_token()

    # call some nested command via HTTP client
    client = HTTPClient.new();
    resp = client.get(CALL_URI, header: { 'Authorization' => "Bearer #{token}", 'Accept' => 'application/json' })

    dtoOut = {}
    dtoOut[:nested_call_status] = resp.status
    dtoOut[:nested_call_result] = resp.content
    ctrl_env.result = dtoOut
  end

end

Call uuApp Controller with HTTPClient and uuOIDC Session

The operation Session.get_call_token() could be used to obtain secure token that should be used for remote call to uuAppServer. Server call have to be requested with the standard HTTP “Authorization” header with “Bearer “ prefix for token in value.

require 'uu_oidc'


# Change to your user credentials
CREDENTIALS = { access_code1: 'user_frog', access_code2: 'Frog2016Secret$' }
UU_APP_UC_URI = UuOidc::Discovery::Discovery.new().[:userinfo_endpoint]

ses=UuOidc::Session.(CREDENTIALS)
token = ses.get_call_token()

# call some command
client = HTTPClient.new();
resp = client.get(UU_APP_UC_URI, header: { 'Authorization' => "Bearer #{token}", 'Accept' => 'application/json' })
puts("uuApp response: #{resp.status}, #{resp.content}")

Call uuApp Controller with uuApp Client and uuOIDC Session

For uuApp Client is prepared interceptor handler that adds the HTTP Authorization header to each remote call with token from specified session.

require 'uu_oidc'
require 'uu_app_core-appclient'

# Change to your user credentials
CREDENTIALS = { access_code1: 'user_frog', access_code2: 'Frog2016Secret$' }
UU_APP_UC_URI = UuOidc::Discovery::Discovery.new().[:userinfo_endpoint]

ses=UuOidc::Session.(CREDENTIALS)

# call some command
client = UuApp::AppClient::Client.new();
client.add_interceptor(UuOidc::AppSession::AppClientSessionHandler);
result = client.get(UU_APP_UC_URI, { session: ses });
puts("Result: #{result}");

Call uu8 Command with HTTPClient and uuOIDC Session

The uu8 Command APIs supports integration with uuOIDC authentication and so the Session call token could be used for uu8 invocation.

require 'uu_oidc'

# Change to your user credentials
CREDENTIALS = { access_code1: 'user_frog', access_code2: 'Frog2016Secret$' }
UU8_COMMAND_URI = 'https://api.plus4u.net/ues/wcp/ues/core/security/session/UESSession/getPersonalRole'

ses=UuOidc::Session.(CREDENTIALS);
token = ses.get_call_token();

client = HTTPClient.new();
resp = client.get(UU8_COMMAND_URI, header: { 'Authorization' => "Bearer #{token}", 'Accept' => 'application/json' });
puts("UU8 Command response: #{resp.status}, #{resp.content}");

Call uu8 Command with UU_OS API and uuOIDC Session

The uu8 client libraries use uu8 Session internally, so for this scenarios the uuOIDC Call token have to be transformed to uu8 call token first.

require 'uu_os'
require 'uu_oidc'

# Change to your user credentials
CREDENTIALS = { access_code1: 'user_frog', access_code2: 'Frog2016Secret$' }

ses=UuOidc::Session.(CREDENTIALS);
token = ses.get_call_token();

# UU8 Commands API requires use of UU::OS:Session  => use OIDC-Token as supported "HTTP authorization token"
# https://api.plus4u.net/uuos/UU/OS/Security/Session.html#login-class_method
UU::OS::Security::Session.("Bearer #{token}");
person = UU::OS::Security::Session.get_personal_role();
puts("Welcome: #{person}");

Client uuOIDC Session login change options

Change default settings is sometimes needed for Session instance - like for testing or staging environments. Use parameter options for this. Next sample shows how to change OIDC server URI and disable SSL certificate verification.

require 'uu_oidc'


ses=UuOidc::Session.(access_code1: 'user_frog',
                          access_code2: 'Frog2016Secret$',
                          options:{uu_oidc_server_uri: 'https://oidc.plus4u.net/uu-oidcg01-main/84723967990075193-a763a18b17d44725b13e00d5f2ab7cef',
                                   uu_oidc_client_ssl_verify_mode:OpenSSL::SSL::VERIFY_NONE })
puts("Welcome: #{ses.identity}")

What next

Licence

The uuOpenIdConnect Client is under UAFL (Unicorn Application Framework Licence) derived from BSD.

(c) 2017 https://unicorn.com