diff --git a/.gitignore b/.gitignore
index 538ef73e5af708403be0f1e6c5e620d5d2aace22..5a5849496455a33f92a446455710a28ed4d349e8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ MANIFEST
 .tox
 build
 .coverage
+venv
diff --git a/CHANGELOG b/CHANGELOG
index dffa7b154619e6fbc7860485131ebf0aa4c9ab8c..fd4eadc3b01dfc82f71a302a133d70907b4611cf 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,11 @@
 django-ucamlookup changelog
 =============================
 
+2.0.0 - 12/03/2019
+------------------
+
+- Add support for authentication. This allows connectivity to the lookup API from outside the CUDN
+
 1.9.5 - 19/12/2018
 ------------------
 
@@ -14,7 +19,8 @@ django-ucamlookup changelog
 1.9.3 - 22/03/2017
 ------------------
 
-- bugfix: "single select" control not initialising (introduced single_user on ucamlookup_users.html to initialise "single select")
+- bugfix: "single select" control not initialising (introduced single_user on ucamlookup_users.html to
+initialise "single select")
 
 1.9.2 - 18/01/2017
 ------------------
diff --git a/README.md b/README.md
index 442d0ad4f0e955b015a17edb44e28f1d726f3760..4928c8548e84e71dc5f4ae75df58ee4bfc3cf624 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,25 @@ django-ucamlookup is a library which provides useful methods and templates to in
 [Django](https://www.djangoproject.com/) application with the University of Cambridge University 
 [Lookup service](https://www.lookup.cam.ac.uk/). 
 
+## Configuration
+
+The following parameters are optional configurations that you can use in your django settings.
+
+* ``UCAMLOOKUP_HOST``. Optional. Default: ``"www.lookup.cam.ac.uk"``. Specifies the hostname used for the 
+IbisClientConnection. This is the connection object that will be used to make all API calls related to lookup.
+* ``UCAMLOOKUP_PORT``. Optional. Default: ``443``. Specifies the port used for the 
+IbisClientConnection. This is the connection object that will be used to make all API calls related to lookup.
+* ``UCAMLOOKUP_URL_BASE``. Optional. Default: ``""``. Specifies the URL base used for the 
+IbisClientConnection. This is the connection object that will be used to make all API calls related to lookup.
+* ``UCAMLOOKUP_CHECK_CERTS``. Optional. Default: ``True``. Indicates if the client should check if the server side
+certificates are valid.
+* ``UCAMLOOKUP_USERNAME``. Optional. Default: ``None``. Specifies the username used for the 
+IbisClientConnection. This is the connection object that will be used to make all API calls related to lookup. We 
+recommend the use of Lookup groups for authentication instead of an individual Raven account.
+* ``UCAMLOOKUP_PASSWORD``. Optional. Default: ``None``. Specifies the password used for the 
+IbisClientConnection. This is the connection object that will be used to make all API calls related to lookup. We 
+recommend the use of Lookup groups passwords for authentication instead of an individual Raven account password.
+
 ## Use
 
 Install django-ucamlookup using pip:
@@ -33,11 +52,21 @@ and the urls entries in the urls.py file:
     )
 ```
 
-## Requirements and warning
+## Warning
+
+Lookup contains personal data of University of Cambridge members. Make sure that you are only showing this data to 
+users with rights to see this data.
+
+## Networking
+
+If no optional settings are specified, django-ucamlookup will use ``anonymous`` as username and no password when 
+setting up an IbisClientConnection and executing Lookup APIs. This type of anonymous conneciton is only available
+inside the Cambridge University Network (CUDN). If your application is deployed outside the CUDN you should use the 
+optional authentication with ``UCAMLOOKUP_USERNAME`` and  ``UCAMLOOKUP_PASSWORD``.
+
+We do not recommend the use of individual Raven accounts and instead to set up a Lookup group. Users can generate a 
+password for the group and use the group short name as a username for authentication.
 
-This module will only work inside the University of Cambridge network. Make sure your users are authenticated as 
-University of Cambridge users (you can use the django-ucamwebauth module) or you will be exposing personal data to 
-non authorised users.
 
 ## Lookup User
 
diff --git a/README.rst b/README.rst
deleted file mode 100644
index 14985f807481a25e942a5107a6297903d9124b16..0000000000000000000000000000000000000000
--- a/README.rst
+++ /dev/null
@@ -1,202 +0,0 @@
-Introduction
-============
-
-django-ucamlookup is a library which provides useful methods and
-templates to integrate your `Django <https://www.djangoproject.com/>`__
-application with the University of Cambridge University `Lookup
-service <https://www.lookup.cam.ac.uk/>`__.
-
-Use
----
-
-Install django-ucamlookup using pip:
-
-.. code:: bash
-
-    pip install django-ucamlookup
-
-Add django-ucamlookup to your installed applications in your project
-configuration settings.py:
-
-.. code:: python
-
-        INSTALLED_APPS=(
-        ...
-            'ucamlookup', 
-        ...
-        ),
-
-and the urls entries in the urls.py file:
-
-.. code:: python
-
-        urlpatterns = patterns(
-        ...
-            # lookup/ibis urls
-            url(r'^ucamlookup/', include('ucamlookup.urls')),
-        ...
-        )
-
-Requirements and warning
-------------------------
-
-This module will only work inside the University of Cambridge network.
-Make sure your users are authenticated as University of Cambridge users
-(you can use the django-ucamwebauth module) or you will be exposing
-personal data to non authorised users.
-
-Lookup User
------------
-
-django-ucamlookup modifies a User object each time is going to be saved,
-either new or update, and assigns to its *last\_name* property the
-visible name from lookup for that user. The username is used to search
-for this user in lookup.
-
-Lookup Group
-------------
-
-django-ucamlookup includes a new model called LookupGroup that it is
-used to cache lookup models. It is used to store the lookup group id and
-its name, and therefore used to reduce the number of call to the lookup
-service. It can also be used to create relation with other models. For
-example, let's say we have a model called Secret and we only want to let
-access to it to users inside a certain group or groups. We will create a
-ManyToMany relation from Secrets to LookupGroup.
-
-The name of the group is retrieve from the lookup service each time the
-group is saved (new or updated). The name is stored in the *name*
-property of the class and the id of the lookup group is stored in
-*lookup\_id*.
-
-It is important to say that this model is not used to cache relations
-between lookup users and lookup groups. These relations are always
-queried to the live lookup service. The model is only used to let the
-developer make relations between models that include lookup groups and
-cache the name of the group.
-
-Template macros
----------------
-
-Two macros are available to be used in a template: ucamlookup\_users,
-and ucamlookup\_groups. These macros have javascript functions that will
-modify a html input tag to an interactive ajax box with interaction to
-the lookup service that will let the user use autocomplete and search
-for lookup users and groups.
-
-If you want to include an input box to let the user search and introduce
-a single user or a list of users, use the ucamlookup\_users macro. You
-should pass as parameters to the macro the html input tag *id* that you
-want to modify/use and if you want to let the user select one or more
-users with the parameter *multiple*:
-
-.. code:: python
-
-        {% include 'ucamlookup_users.html' with input_tag_id="lookup_users" multiple=true user_list="authors" %}
-
-If you want to show existing records in the input tag you will need to
-pass to the view the list of crsids. This list needs to be passed inside
-a dictionary called *loockup\_lists*. The key entry name of the
-dictionary where the list is located it is passed to the macro using the
-variable *user\_list* as shown previously. In this example:
-
-.. code:: python
-
-        lookup_lists = {
-            'authors': post.users.all(),
-        }
-
-You will also have to include the following macro in the head of your
-template to load the js and css files associated. These macros require
-jquery if you want to include your own jquery library or you are already
-using it in your template use the parameter *jquery* to specify it.
-
-.. code:: python
-
-        {% include 'ucamlookup_headers.html' with jquery=True %}
-
-And your input tag will be transform into an ajax box that allows the
-user to search for users using lookup either using their username or
-their complete name. A list of crsids will be sent as the value of the
-input tag.
-
-The same will work for lookup groups, just substitute user by group in
-the id and in the include.
-
-Admin interface
----------------
-
-The admin interface is tunned to add managing options for the
-LookupGroup model. The *add* option will show the same
-ajax-lookup-integrated-input as the template macros described above.
-
-It also changes the add form for the user and it also shows an
-interactive ajax lookup-integrated input form when the admin wants to
-add a new user to the app.
-
-These input forms allow to search for name and crsid in the case of a
-new user and for name in the case of a lookup group.
-
-Available functions
--------------------
-
-The module also provides some useful functions to use in your app that
-do all the calls to the lookup service needed.
-
-*get\_group\_ids\_of\_a\_user\_in\_lookup(user)*: Returns the list of
-group ids of a user
-
-*user\_in\_groups(user, lookup\_groups)*: Check in the lookup webservice
-if the user is member of any of the groups in the LookupGroup list
-passed by parameter. Returns True if the user is in any of the groups or
-False otherwise
-
-*def get\_institutions(user=None)*: Returns the list of institutions
-using the lookup ucam service. The institutions of the user passed by
-parameters will be shown first in the list returned
-
-*validate\_crsids(crsids\_text)*: It receives a list of crsids (comming
-from input tag from the template macros described previously) [wich
-format is separated by commas and with no spaces in between] and returns
-a list of Users corresponding to the crsids passed.
-
-*get\_or\_create\_user\_by\_crsid(crsid)*: Returns the User
-corresponding to the crsid passed. If it does not exists in the
-database, it is created.
-
-*get\_institution\_name\_by\_id(institution\_id,
-all\_institutions=None)*: Returns the name of an institution by the id
-passed. If all\_institutions is passed (the result from
-get\_institutions) then the search is done locally using this list
-instead of a lookup call.
-
-The last two methods can be used to add institutions to a model and show
-the name instead of the code in the admin interface
-
-.. code:: python
-
-    class MyModelAdmin(ModelAdmin):
-        all_institutions = get_institutions()
-        
-        model = MyModel
-        list_display = ('institution', )
-        list_filter = ('institution_id', )
-
-        def institution(self, obj):
-            return get_institution_name_by_id(obj.institution_id, self.all_institutions)
-            
-        institution.admin_order_field = 'institution_id'
-
-
-Developing
-==========
-
-Run tests
----------
-
-Tox is configured to run on a container with a matrix execution of different versions of python and django combined.
-It will also show the coverage and any possible PEP8 violations.
-
-.. code:: shell
-
-        $ docker-compose up
diff --git a/setup.py b/setup.py
index 7f700502cd5758674ad0799c0d14c74e6f858acf..2737e04ef86f87a3d3f09723982bcbefd2ea7429 100755
--- a/setup.py
+++ b/setup.py
@@ -6,9 +6,9 @@ from setuptools import find_packages
 setup(
     name='django-ucamlookup',
     description='A Django module for the University of Cambridge Lookup service',
-    long_description=open('README.rst').read(),
+    long_description=open('README.md').read(),
     url='https://github.com/uisautomation/django-ucamlookup.git',
-    version='1.9.5',
+    version='2.0.0',
     license='MIT',
     author='Information Systems Group, University Information Services, University of Cambridge',
     author_email='devops@uis.cam.ac.uk',
diff --git a/ucamlookup/tests.py b/ucamlookup/tests.py
index 5a3b8f012957d50e7ac3c7e6274db155dfd7ab3b..2a9ce80eec6374979f3967c3a9b01a48a69d62f7 100644
--- a/ucamlookup/tests.py
+++ b/ucamlookup/tests.py
@@ -6,15 +6,27 @@ try:
     from django.core.urlresolvers import reverse
 except Exception:
     from django.urls import reverse
-from django.test import TestCase
+from django.test import TestCase, override_settings
 from ucamlookup.models import LookupGroup
 from ucamlookup.utils import user_in_groups, get_users_from_query, return_visibleName_by_crsid, get_groups_from_query, \
     return_title_by_groupid, get_group_ids_of_a_user_in_lookup, get_institutions, get_institution_name_by_id, \
-    validate_crsids
+    validate_crsids, get_connection
 
 
 class UcamLookupTests(TestCase):
 
+    @override_settings(UCAMLOOKUP_HOST="mock_host", UCAMLOOKUP_PORT=80, UCAMLOOKUP_URL_BASE="/mock",
+                       UCAMLOOKUP_CHECK_CERTS=False, UCAMLOOKUP_USERNAME="mock_username",
+                       UCAMLOOKUP_PASSWORD="mock_password")
+    def test_optional_settins(self):
+            conn = get_connection()
+            self.assertEqual(conn.host, "mock_host")
+            self.assertEqual(conn.port, 80)
+            self.assertEqual(conn.url_base, "/mock/")
+            self.assertIsNone(conn.ca_certs)
+            self.assertEqual(conn.username, "mock_username")
+            self.assertEqual(conn.password, "mock_password")
+
     def test_add_name_to_user_and_add_title_to_group(self):
         with self.assertRaises(User.DoesNotExist):
             User.objects.get(username="amc203")
diff --git a/ucamlookup/utils.py b/ucamlookup/utils.py
index c0a6552bfbf85b301611086e2bbf72044b401ef8..edcb37f687c3a1cb8aef82929059d155b9de46e3 100644
--- a/ucamlookup/utils.py
+++ b/ucamlookup/utils.py
@@ -1,22 +1,41 @@
 import re
+
+from django.conf import settings
 from django.core.exceptions import ValidationError
-from ucamlookup.ibisclient import createConnection, PersonMethods, GroupMethods, IbisException, InstitutionMethods
+from ucamlookup.ibisclient import PersonMethods, GroupMethods, IbisException, InstitutionMethods, \
+    IbisClientConnection
+
+
+def get_connection():
+    UCAMLOOKUP_HOST = getattr(settings, "UCAMLOOKUP_HOST", "www.lookup.cam.ac.uk")
+    UCAMLOOKUP_PORT = getattr(settings, 'UCAMLOOKUP_PORT', 443)
+    UCAMLOOKUP_URL_BASE = getattr(settings, 'UCAMLOOKUP_URL_BASE', "")
+    UCAMLOOKUP_CHECK_CERTS = getattr(settings, 'UCAMLOOKUP_CHECK_CERTS', True)
+    UCAMLOOKUP_USERNAME = getattr(settings, 'UCAMLOOKUP_USERNAME', None)
+    UCAMLOOKUP_PASSWORD = getattr(settings, 'UCAMLOOKUP_PASSWORD', None)
+
+    conn = IbisClientConnection(UCAMLOOKUP_HOST, UCAMLOOKUP_PORT, UCAMLOOKUP_URL_BASE, UCAMLOOKUP_CHECK_CERTS)
+
+    if UCAMLOOKUP_USERNAME:
+        conn.set_username(UCAMLOOKUP_USERNAME)
 
+    if UCAMLOOKUP_PASSWORD:
+        conn.set_password(UCAMLOOKUP_PASSWORD)
 
-conn = createConnection()
+    return conn
 
 
 def get_users_from_query(search_string):
     """ Returns the list of people based on the search string using the lookup ucam service
         :param search_string: the search string
     """
-    persons = PersonMethods(conn).search(query=search_string)
+    persons = PersonMethods(get_connection()).search(query=search_string)
 
     return list(map((lambda person: {'crsid': person.identifier.value, 'visibleName': person.visibleName}), persons))
 
 
 def return_visibleName_by_crsid(crsid):
-    person = PersonMethods(conn).getPerson(scheme='crsid', identifier=crsid)
+    person = PersonMethods(get_connection()).getPerson(scheme='crsid', identifier=crsid)
     return person.visibleName if person is not None else ''
 
 
@@ -24,13 +43,13 @@ def get_groups_from_query(search_string):
     """ Returns the list of groups based on the search string using the lookup ucam service
         :param search_string: the search string
     """
-    groups = GroupMethods(conn).search(query=search_string)
+    groups = GroupMethods(get_connection()).search(query=search_string)
 
     return list(map((lambda group: {'groupid': group.groupid, 'title': group.title}), groups))
 
 
 def return_title_by_groupid(groupid):
-    group = GroupMethods(conn).getGroup(groupid=groupid)
+    group = GroupMethods(get_connection()).getGroup(groupid=groupid)
     # TODO If a group does not exists in lookup should we allow it?
     if group is None:
         raise ValidationError("The group with id %(groupid)s does not exist in Lookup", code='invalid',
@@ -44,7 +63,7 @@ def get_group_ids_of_a_user_in_lookup(user):
     :return: the list of group_ids
     """
     try:
-        group_list = PersonMethods(conn).getGroups(scheme="crsid", identifier=user.username)
+        group_list = PersonMethods(get_connection()).getGroups(scheme="crsid", identifier=user.username)
         return list(map(lambda group: group.groupid, group_list))
     except IbisException:
         return []
@@ -70,7 +89,7 @@ def get_user_lookupgroups(user):
     :return: the list of LookupGroups
     """
     try:
-        group_list = PersonMethods(conn).getGroups(scheme="crsid", identifier=user.username)
+        group_list = PersonMethods(get_connection()).getGroups(scheme="crsid", identifier=user.username)
         return list(map(lambda group: get_or_create_group_by_groupid(group.groupid), group_list))
     except IbisException:
         return []
@@ -82,14 +101,14 @@ def get_institutions(user=None):
         :param user: the user doing the request
     """
 
-    all_institutions = InstitutionMethods(conn).allInsts(includeCancelled=False)
+    all_institutions = InstitutionMethods(get_connection()).allInsts(includeCancelled=False)
     # filter all the institutions that were created for store year students
     all_institutions = list(filter(lambda institution: re.match(r'.*\d{2}$', institution.id) is None,
                                    all_institutions))
 
     if user is not None:
         try:
-            all_institutions = PersonMethods(conn).getInsts("crsid", user.username) + all_institutions
+            all_institutions = PersonMethods(get_connection()).getInsts("crsid", user.username) + all_institutions
         except IbisException:
             pass
 
@@ -100,7 +119,7 @@ def get_institution_name_by_id(institution_id, all_institutions=None):
     if all_institutions is not None:
         instname = next((institution[1] for institution in all_institutions if institution[0] == institution_id), None)
     else:
-        institution = InstitutionMethods(conn).getInst(instid=institution_id)
+        institution = InstitutionMethods(get_connection()).getInst(instid=institution_id)
         instname = institution.name if institution is not None else None
 
     return instname if instname is not None else 'This institution no longer exists in the database'
@@ -168,4 +187,4 @@ def get_users_of_a_group(group):
     """
 
     return list(map(lambda user: get_or_create_user_by_crsid(user.identifier),
-                    GroupMethods(conn).getMembers(groupid=group.groupid)))
+                    GroupMethods(get_connection()).getMembers(groupid=group.groupid)))