diff --git a/hiboo/__init__.py b/hiboo/__init__.py
index 6033fb783dd2b77cbfc80bc8f727f7553566162b..93c9c49dd32e3c069ff71a0f762070fb8b4315cd 100644
--- a/hiboo/__init__.py
+++ b/hiboo/__init__.py
@@ -32,11 +32,12 @@ def create_app_from_config(config):
         return dict(config=app.config, utils=utils)
 
     # Import views
-    from hiboo import account, user, profile, service, sso, captcha, api
+    from hiboo import account, user, profile, service, application, sso, captcha, api
     app.register_blueprint(account.blueprint, url_prefix='/account')
     app.register_blueprint(user.blueprint, url_prefix='/user')
     app.register_blueprint(profile.blueprint, url_prefix='/profile')
     app.register_blueprint(service.blueprint, url_prefix='/service')
+    app.register_blueprint(application.blueprint, url_prefix='/application')
     app.register_blueprint(sso.blueprint, url_prefix='/sso')
     app.register_blueprint(captcha.blueprint, url_prefix='/captcha')
     app.register_blueprint(api.blueprint, url_prefix='/api')
diff --git a/hiboo/application/__init__.py b/hiboo/application/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..8343071bfb5329dc4f1c03f9866b295f22794f2c
--- /dev/null
+++ b/hiboo/application/__init__.py
@@ -0,0 +1,12 @@
+import flask
+
+
+blueprint = flask.Blueprint("application", __name__, template_folder="templates")
+
+
+from hiboo.application import base
+
+register = base.BaseApplication.register
+registry = base.BaseApplication.registry
+
+from hiboo.application import sso, social, storage
\ No newline at end of file
diff --git a/hiboo/application/base.py b/hiboo/application/base.py
new file mode 100644
index 0000000000000000000000000000000000000000..3b1bbb4c44fcc3fae5c53350823d16a24f12dda5
--- /dev/null
+++ b/hiboo/application/base.py
@@ -0,0 +1,39 @@
+from hiboo.service.forms import ServiceForm as BaseForm
+from flask_babel import lazy_gettext as _
+
+import flask
+
+
+class BaseApplication(object):
+    """ Base application class, that provides basic behavior and registry
+    """
+
+    registry = dict()
+    sso_protocol = None
+    name = None
+
+    @classmethod
+    def register(cls, application_id):
+        def register_function(application):
+            application.application_id = application_id
+            cls.registry[application_id] = application()
+            return application
+        return register_function
+
+    def render_details(self, service):
+        return flask.render_template(
+            "application_{}.html".format(self.application_id),
+            service=service
+        )
+
+    def fill_service(self, service):
+        return self.sso_protocol.fill_service(service)
+
+
+class OIDCApplication(BaseApplication):
+    sso_protocol = "oidc"
+
+class SAMLApplication(BaseApplication):
+    sso_protocol = "saml"
+
+
diff --git a/hiboo/application/social.py b/hiboo/application/social.py
new file mode 100644
index 0000000000000000000000000000000000000000..c023e433b2b9ff0155cdd2d0654b58723a58b790
--- /dev/null
+++ b/hiboo/application/social.py
@@ -0,0 +1,32 @@
+from hiboo.application import register, base
+from wtforms import validators, fields
+from flask_babel import lazy_gettext as _
+
+
+@register("mastodon")
+class MastodonApplication(base.SAMLApplication):
+    """ Mastodon social network is an ActivityPub micro-blogging platform
+    """
+
+    name = _("Mastodon social network")
+
+    class Form(base.BaseForm):
+        application_uri = fields.StringField(_("Mastodon URL"), [validators.URL(require_tld=False)])
+        submit = fields.SubmitField(_('Submit'))
+
+    def populate_service(self, form, service):
+        service.profile_regex = "[a-z0-9_]+([a-z0-9_\.-]+[a-z0-9_]+)?"
+        callback_uri = form.application_uri.data + "/auth/auth/callback"
+        service.config.update({
+            "application_uri": form.application_uri.data,
+            "acs": callback_uri,
+            "entityid": callback_uri,
+            "sign_mode": "assertion"
+        })
+        self.fill_service(service)
+
+    def populate_form(self, service, form):
+        form.process(
+            obj=service,
+            application_uri=service.config.get("application_uri")
+        )
diff --git a/hiboo/application/sso.py b/hiboo/application/sso.py
new file mode 100644
index 0000000000000000000000000000000000000000..9f7feebbf9135ac902f3b4de95060a25fe05ac2d
--- /dev/null
+++ b/hiboo/application/sso.py
@@ -0,0 +1,97 @@
+from hiboo.application import base, register
+from wtforms import validators, fields
+from flask_babel import lazy_gettext as _
+
+
+@register("oidc")
+class GenericOIDCApplication(base.OIDCApplication):
+    """ OpenID Connect (OIDC) is JWT based authentication and authorization protocol 
+    """
+
+    name = _("Generic OIDC")
+
+    class Form(base.BaseForm):
+        redirect_uri = fields.StringField(_("Redirect URI"), [validators.URL(require_tld=False)])
+        token_endpoint_auth_method = fields.SelectField(
+            _('Token Endpoint Auth Method'), choices=[
+                ("client_secret_post", _("HTTP POST data")),
+                ("client_secret_basic", _("HTTP basic authorization")),
+                ("none", _("No authentication"))
+            ]
+        )
+        grant_types = fields.SelectMultipleField(
+            _('OpenID Connect grant type'), choices=[
+                ("authorization_code", _("Authorization Code")),
+                ("implicit", _("Implicit")),
+                ("hybrid", _("Hybrid"))
+            ]
+        )
+        response_types = fields.SelectMultipleField(
+            _('Allowed response types'), choices=[
+                ("code", _("Authorization code only")),
+                ("id_token", _("Id token only")),
+                ("id_token token", _("Id token and token"))
+            ]
+        )
+        special_mappings = fields.SelectMultipleField(
+            _('Enabled special claim mappings'), choices=[
+                ("mask_sub_uuid", _("Mask the profile uuid")),
+                ("original_email", _("Return the actual user email"))
+            ]
+        )
+        submit = fields.SubmitField(_('Submit'))
+
+    def populate_service(self, form, service):
+        service.config.update({
+            "token_endpoint_auth_method": form.token_endpoint_auth_method.data,
+            "redirect_uris": [form.redirect_uri.data],
+            "grant_types": form.grant_types.data,
+            "response_types": form.response_types.data,
+            "special_mappings": form.special_mappings.data
+        })
+        self.fill_service(service)
+
+    def populate_form(self, service, form):
+        form.process(
+            obj=service,
+            token_endpoint_auth_method=service.config.get("token_endpoint_auth_method"),
+            redirect_uri=service.config.get("redirect_uris", [""])[0],
+            grant_types=service.config.get("grant_types", ["authorization_code"]),
+            response_types=service.config.get("response_types", ["code"]),
+            special_mappings=service.config.get("special_mappings", [])
+        )
+
+
+@register("saml")
+class GenericSAMLApplication(base.SAMLApplication):
+    """ SAML2 is a legacy protocol based on XML security. Only redirect/post binding is supported
+    """
+
+    name = _("Generic SAML2")
+
+    class Form(base.BaseForm):
+        entityid = fields.StringField(_('SP entity id'), [validators.URL(require_tld=False)])
+        acs = fields.StringField(_('SP ACS'), [validators.URL(require_tld=False)])
+        sign_mode = fields.SelectField(
+            _('Signature mode'), choices=[
+                ('response', _('Sign the full response')),
+                ('assertion', _('Sign only the assertion'))
+            ]
+        )
+        submit = fields.SubmitField(_('Submit'))
+
+    def populate_service(self, form, service):
+        service.config.update({
+            "acs": form.acs.data,
+            "entityid": form.entityid.data,
+            "sign_mode": form.sign_mode.data
+        })
+        self.fill_service(service)
+
+    def populate_form(self, service, form):
+        form.process(
+            obj=service,
+            acs=service.config.get("acs"),
+            entityid=service.config.get("entityid"),
+            sign_mode=service.config.get("sign_mode")
+        )
\ No newline at end of file
diff --git a/hiboo/application/storage.py b/hiboo/application/storage.py
new file mode 100644
index 0000000000000000000000000000000000000000..1d5c45fb8c061dccfd65dac2c7430d217da62e95
--- /dev/null
+++ b/hiboo/application/storage.py
@@ -0,0 +1,34 @@
+from hiboo.application import register, base
+from wtforms import validators, fields
+from flask_babel import lazy_gettext as _
+
+
+@register("gitlab")
+class GitlabApplication(base.OIDCApplication):
+    """ Gitlab is a source code and project management plaform, largely based on Git
+    """
+
+    name = _("Gitlab repository manager")
+
+    class Form(base.BaseForm):
+        application_uri = fields.StringField(_("Gitlab URL"), [validators.URL(require_tld=False)])
+        submit = fields.SubmitField(_('Submit'))
+
+    def populate_service(self, form, service):
+        service.profile_regex = "[a-z0-9_.\-]*"
+        callback_uri = form.application_uri.data + "/users/auth/openid_connect/callback"
+        service.config.update({
+            "application_uri": form.application_uri.data,
+            "token_endpoint_auth_method": "client_secret_post",
+            "redirect_uris": [callback_uri],
+            "grant_types": ["authorization_code"],
+            "response_types": ["code"],
+            "special_mappings": ["mask_sub_uuid"]
+        })
+        self.fill_service(service)
+
+    def populate_form(self, service, form):
+        form.process(
+            obj=service,
+            application_uri=service.config.get("application_uri")
+        )
diff --git a/hiboo/application/templates/application_gitlab.html b/hiboo/application/templates/application_gitlab.html
new file mode 100644
index 0000000000000000000000000000000000000000..9d28220dee4cc6ccf9e51e26c2f74612ea351540
--- /dev/null
+++ b/hiboo/application/templates/application_gitlab.html
@@ -0,0 +1,38 @@
+<h3>Setting up Gitlab</h3>
+<p>Gitlab supports OIDC authentication using the Omniauth::oidc module.</p>
+<p>If you are using Omnibus, you may paste the following config directly in your `gitlab.rb`.</p>
+<pre>
+# Authentication
+gitlab_rails['omniauth_allow_single_sign_on'] = ['openid_connect']
+gitlab_rails['omniauth_block_auto_created_users'] = false
+gitlab_rails['omniauth_auto_sign_in_with_provider'] = 'openid_connect'
+gitlab_rails['omniauth_providers'] = [
+    { 'name' => 'openid_connect',
+      'label' => 'Hiboo',
+      'args' => {
+        'name' => 'openid_connect',
+        'scope' => ['openid','profile','email'],
+        'issuer' => '{{ url_for("sso.oidc_authorize", service_uuid=service.uuid, _external=True) }}',
+        'response_type' => 'code',
+        'discovery' => false,
+        'client_auth_method' => 'query',
+        'client_options' => {
+        'identifier' => '{{ service.config["client_id"] }}',
+        'secret' => '{{ service.config["client_secret"] }}',
+        'redirect_uri' => '{{ service.config["redirect_uris"][0] }}',
+        'authorization_endpoint' => '{{ url_for("sso.oidc_authorize", service_uuid=service.uuid, _external=True) }}',
+        'token_endpoint' => '{{ url_for("sso.oidc_token", service_uuid=service.uuid, _external=True) }}',
+        'userinfo_endpoint' => '{{ url_for("sso.oidc_userinfo", service_uuid=service.uuid, _external=True) }}'
+        }
+      }
+    }
+]
+</pre>
+
+<p></p>You will also need to provision your users Omniauth bindings, by running the following SQL query against your Gitlab database.</p>
+
+<pre>
+insert into identities (extern_uid,provider,user_id,created_at,updated_at) (select users.username as extern_uid, 'openid_connect' as provider, users.id as user_id, now() created_at, now() updated_at from users);
+</pre>
+
+{% include "application_oidc.html" %}
\ No newline at end of file
diff --git a/hiboo/application/templates/application_mastodon.html b/hiboo/application/templates/application_mastodon.html
new file mode 100644
index 0000000000000000000000000000000000000000..60d1047924011ffdc642a016bb41c75a2723a494
--- /dev/null
+++ b/hiboo/application/templates/application_mastodon.html
@@ -0,0 +1,22 @@
+<h3>Setting up Mastodon</h3>
+<p>Mastodon uses SAML for SSO authentication. The example configuration is available at the following URL : <a href="https://github.com/tootsuite/mastodon/blob/master/.env.production.sample">https://github.com/tootsuite/mastodon/blob/master/.env.production.sample</a>.</p>
+<p>In order to configure SAML for Mastodon, you may copy then paste the following lines directly into your Mastodon environment.</p>
+<pre>
+# Authentication
+OAUTH_REDIRECT_AT_SIGN_IN=true
+SAML_ENABLED=true
+SAML_ISSUER={{ service.config["sp_entityid"] }}
+SAML_ATTRIBUTES_STATEMENTS_UID=urn:oid:0.9.2342.19200300.100.1.1
+SAML_ATTRIBUTES_STATEMENTS_EMAIL=urn:oid:1.2.840.113549.1.9.1.1
+SAML_UID_ATTRIBUTE=urn:oid:0.9.2342.19200300.100.1.1
+SAML_ALLOWED_CLOCK_DRIFT=60
+SAML_SECURITY_WANT_ASSERTION_SIGNED=true
+SAML_SECURITY_WANT_ASSERTION_ENCRYPTED=true
+SAML_SECURITY_ASSUME_EMAIL_IS_VERIFIED=true
+SAML_IDP_SSO_TARGET_URL={{ url_for("sso.saml_redirect", service_uuid=service.uuid, _external=True) }}
+SAML_IDP_CERT={{ "".join(service.config["idp_cert"].strip().split("\n")[1:-1]) }}
+SAML_CERT={{ "".join(service.config["sp_cert"].strip().split("\n")[1:-1]) }}
+SAML_PRIVATE_KEY={{ "".join(service.config["sp_key"].strip().split("\n")[1:-1]) }}
+</pre>
+
+{% include "application_saml.html" %}
\ No newline at end of file
diff --git a/hiboo/application/templates/application_oidc.html b/hiboo/application/templates/application_oidc.html
new file mode 100644
index 0000000000000000000000000000000000000000..00e406337911690aec0ced28bd4f434c07d909d7
--- /dev/null
+++ b/hiboo/application/templates/application_oidc.html
@@ -0,0 +1,15 @@
+<h3>Detailed OpenID Connect settings</h3>
+<dt>{% trans %}Authorization endpoint{% endtrans %}</dt>
+<dd><pre>{{ url_for("sso.oidc_authorize", service_uuid=service.uuid, _external=True) }}</pre></dd>
+
+<dt>{% trans %}Token endpoint{% endtrans %}</dt>
+<dd><pre>{{ url_for("sso.oidc_token", service_uuid=service.uuid, _external=True) }}</pre></dd>
+
+<dt>{% trans %}Userinfo endpoint{% endtrans %}</dt>
+<dd><pre>{{ url_for("sso.oidc_userinfo", service_uuid=service.uuid, _external=True) }}</pre></dd>
+
+<dt>{% trans %}Client ID{% endtrans %}</dt>
+<dd><pre>{{ service.config["client_id"] }}</pre></dd>
+
+<dt>{% trans %}Client secret{% endtrans %}</dt>
+<dd><pre>{{ service.config["client_secret"] }}</dd>
diff --git a/hiboo/application/templates/application_pick.html b/hiboo/application/templates/application_pick.html
new file mode 100644
index 0000000000000000000000000000000000000000..2e5dc6754c60ef6192119a17c01a1efe6dfca968
--- /dev/null
+++ b/hiboo/application/templates/application_pick.html
@@ -0,0 +1,22 @@
+{% extends "base.html" %}
+
+{% block title %}{% trans %}Create a service{% endtrans %}{% endblock %}
+{% block subtitle %}{% trans %}pick an application{% endtrans %}{% endblock %}
+
+{% block content %}
+<div class="row">
+{% for application_id, application in applications.items() %}
+<div class="col-md-4 col-s-6 col-xs-12">
+  <div class="box box-widget widget-user-2">
+    <div class="widget-user-header bg-{{ macros.colors[loop.index0 % 7] }}">
+      <a href="{{ url_for(route, application_id=application_id) }}" style="opacity: 0.8" class="btn btn-lg bg-gray text-black pull-right">
+        {% trans %}Create{% endtrans %}
+      </a>
+      <h3 class="widget-header-username">{{ application.name }}</h3>
+      <h5 class="widget-header-desc">{{ application.__doc__ }}&nbsp;</h5>
+    </div>
+  </div>
+</div>
+{% endfor %}
+</div>
+{% endblock %}
diff --git a/hiboo/application/templates/application_saml.html b/hiboo/application/templates/application_saml.html
new file mode 100644
index 0000000000000000000000000000000000000000..090e9e4768ff47859d89d6b7c9d09b85276ab219
--- /dev/null
+++ b/hiboo/application/templates/application_saml.html
@@ -0,0 +1,18 @@
+<h3>Detailed SAML settings</h3>
+<dt>{% trans %}SAML Metadata{% endtrans %}</dt>
+<dd><pre>{{ url_for("sso.saml_metadata", service_uuid=service.uuid, _external=True) }}</pre></dd>
+
+<dt>{% trans %}SSO redirect binding{% endtrans %}</dt>
+<dd><pre>{{ url_for("sso.saml_redirect", service_uuid=service.uuid, _external=True) }}</pre></dd>
+
+<dt>{% trans %}ACS{% endtrans %}</dt>
+<dd><pre>{{ service.config["acs"] }}</pre></dd>
+
+<dt>{% trans %}IDP certificate{% endtrans %}</dt>
+<dd><pre>{{ service.config["idp_cert"] }}</pre></dd>
+
+<dt{% trans %}>SP certificate{% endtrans %}</dt>
+<dd><pre>{{ service.config["sp_cert"] }}</pre></dd>
+
+<dt>{% trans %}SP private key{% endtrans %}</dt>
+<dd><pre>{{ service.config["sp_key"] }}</pre></dd>
diff --git a/hiboo/models.py b/hiboo/models.py
index 51543ed2e78df897d464a6441752ee8e61c9e7f8..51bde2dd79ae1195f868d0e115ad965af7c75674 100644
--- a/hiboo/models.py
+++ b/hiboo/models.py
@@ -138,11 +138,6 @@ class Service(db.Model):
     """
     __tablename__ = "service"
 
-    APPLICATIONS = {
-        "generic": "Generic application",
-        "mastodon": "Mastodon"
-    }
-
     LOCKED = "locked"
     RESERVED = "reserved"
     MANAGED = "managed"
@@ -157,10 +152,9 @@ class Service(db.Model):
         OPEN: _("No validation is required")
     }
 
-    protocol = db.Column(db.String(25))
     name = db.Column(db.String(255))
     provider = db.Column(db.String(255))
-    application = db.Column(db.String(255))
+    application_id = db.Column(db.String(255))
     description = db.Column(db.String())
     policy = db.Column(db.String(255))
     max_profiles = db.Column(db.Integer(), nullable=False, default=1)
diff --git a/hiboo/service/admin.py b/hiboo/service/admin.py
index 9c70d664e1d4eb497317cc37fbf2220424b21c17..e7219517974bb5f84a0bc296311edcf35acf2758 100644
--- a/hiboo/service/admin.py
+++ b/hiboo/service/admin.py
@@ -1,6 +1,5 @@
-from hiboo import models, utils, security
+from hiboo import models, utils, security, application
 from hiboo.service import blueprint, forms
-from hiboo.sso import protocols
 from flask_babel import lazy_gettext as _
 
 import flask
@@ -15,26 +14,29 @@ def list():
 
 
 @blueprint.route("/create")
-@blueprint.route("/create/<protocol_name>", methods=["GET", "POST"])
+@blueprint.route("/create/<application_id>", methods=["GET", "POST"])
 @security.admin_required()
-def create(protocol_name=None):
-    if protocol_name is None:
-        return flask.render_template("protocol_pick.html", protocols=protocols)
-    protocol = protocols.get(protocol_name, None) or flask.abort(404)
-    form = protocol.Config.derive_form(forms.ServiceForm)()
+def create(application_id=None):
+    if application_id is None:
+        return flask.render_template(
+            "application_pick.html",
+            applications=application.registry,
+            route="service.create"
+        )
+    app = application.registry.get(application_id) or flask.abort(404)
+    form = app.Form()
     if form.validate_on_submit():
         service = models.Service()
-        service.protocol = protocol_name
+        service.application_id = application_id
         service.uuid = str(uuid.uuid4())
         service.config = {}
         form.populate_obj(service)
-        protocol.Config.populate_service(form, service)
+        app.populate_service(form, service)
         models.db.session.add(service)
         models.db.session.commit()
         flask.flash(_("Service successfully created"), "success")
         return flask.redirect(flask.url_for(".list"))
-    return flask.render_template("service_create.html",
-        protocol=protocol, form=form)
+    return flask.render_template("service_create.html", application=app, form=form)
 
 
 @blueprint.route("/edit")
@@ -42,15 +44,15 @@ def create(protocol_name=None):
 @security.admin_required()
 def edit(service_uuid):
     service = models.Service.query.get(service_uuid) or flask.abort(404)
-    protocol = protocols.get(service.protocol, None) or flask.abort(404)
-    form = protocol.Config.derive_form(forms.ServiceForm)()
+    app = application.registry.get(service.application_id) or flask.abort(404)
+    form = app.Form()
     if form.validate_on_submit():
         form.populate_obj(service)
-        protocol.Config.populate_service(form, service)
+        app.populate_service(form, service)
         models.db.session.commit()
         flask.flash(_("Service successfully updated"), "success")
         return flask.redirect(flask.url_for(".details", service_uuid=service_uuid))
-    protocol.Config.populate_form(service, form)
+    app.populate_form(service, form)
     return flask.render_template("service_edit.html", service=service, form=form)
 
 
@@ -58,7 +60,8 @@ def edit(service_uuid):
 @security.admin_required()
 def details(service_uuid):
     service = models.Service.query.get(service_uuid) or flask.abort(404)
-    return flask.render_template("service_details.html", service=service)
+    app = application.registry.get(service.application_id) or flask.abort(404)
+    return flask.render_template("service_details.html", service=service, application=app)
 
 
 @blueprint.route("/delete/<service_uuid>", methods=["GET", "POST"])
diff --git a/hiboo/service/forms.py b/hiboo/service/forms.py
index 86efd8232dd8916a5593e1511b559dbc7b8f02d8..ee17f4a076eacc46e7da8ef458bda7aae91c77e4 100644
--- a/hiboo/service/forms.py
+++ b/hiboo/service/forms.py
@@ -1,7 +1,7 @@
 from wtforms import validators, fields, widgets
 from flask_babel import lazy_gettext as _
 
-from hiboo import models
+from hiboo import models, application
 
 import flask_wtf
 
@@ -10,12 +10,10 @@ class ServiceForm(flask_wtf.FlaskForm):
     name = fields.StringField(_('Service name'), [validators.DataRequired()])
     provider = fields.StringField(_('Provider'), [validators.DataRequired()])
     description = fields.StringField(_('Description'))
-    application = fields.SelectField(_('Application'),
-        choices=list(models.Service.APPLICATIONS.items()))
     policy = fields.SelectField(_('Profile policy'),
         choices=list(models.Service.POLICIES.items()))
     max_profiles = fields.IntegerField(_('Maximum profile count'),
         [validators.NumberRange(1, 1000)])
-    profile_regex = fields.StringField(_('Profile usenrame regex'))
+    profile_regex = fields.StringField(_('Profile username regex'))
     same_username = fields.BooleanField(_('Disable per-profile username'))
     submit = fields.SubmitField(_('Submit'))
diff --git a/hiboo/service/templates/protocol_pick.html b/hiboo/service/templates/protocol_pick.html
deleted file mode 100644
index 788a833d023091f74b4f85ff8b725cd29225e6c7..0000000000000000000000000000000000000000
--- a/hiboo/service/templates/protocol_pick.html
+++ /dev/null
@@ -1,23 +0,0 @@
-{% extends "base.html" %}
-
-{% block title %}{% trans %}Create a service{% endtrans %}{% endblock %}
-{% block subtitle %}{% trans %}pick a protocol{% endtrans %}{% endblock %}
-
-{% block content %}
-<div class="row">
-{% for protocol in protocols %}
-{% import "protocol_" + protocol + ".html" as protocol_macros %}
-<div class="col-md-4 col-s-6 col-xs-12">
-  <div class="box box-widget widget-user-2">
-    <div class="widget-user-header bg-{{ macros.colors[loop.index0 % 7] }}">
-      <a href="{{ url_for(".create", protocol_name=protocol) }}" style="opacity: 0.8" class="btn btn-lg bg-gray text-black pull-right">
-        {% trans %}Create {% endtrans %}{{ protocol_macros.name() }}
-      </a>
-      <h3 class="widget-header-username">{{ protocol_macros.name() }}</h3>
-      <h5 class="widget-header-desc">{{ protocol_macros.description() }}&nbsp;</h5>
-    </div>
-  </div>
-</div>
-{% endfor %}
-</div>
-{% endblock %}
diff --git a/hiboo/service/templates/service_create.html b/hiboo/service/templates/service_create.html
index 05f4402315eef9fdb5f3d952de28aa7fba058bd6..ef13bdb687ed8a345f9f8402936b07c7a9fc8bf3 100644
--- a/hiboo/service/templates/service_create.html
+++ b/hiboo/service/templates/service_create.html
@@ -1,5 +1,5 @@
 {% extends "form.html" %}
 
-{% set protocol_name = protocol.__name__ %}
+{% set application_name = application.name %}
 {% block title %}{% trans %}Create a service{% endtrans %}{% endblock %}
-{% block subtitle %}{% trans protocol_name %}add a {{ protocol_name }} service{% endtrans %}{% endblock %}
+{% block subtitle %}{% trans application_name %}add a {{ application_name }} service{% endtrans %}{% endblock %}
diff --git a/hiboo/service/templates/service_details.html b/hiboo/service/templates/service_details.html
index cdd73733cc78a052b299ea36cdad09cc0d885c13..3a9963dcb4b5d2f6a4f5578ac2e4d63f3bf5fc1c 100644
--- a/hiboo/service/templates/service_details.html
+++ b/hiboo/service/templates/service_details.html
@@ -1,5 +1,4 @@
 {% extends "base.html" %}
-{% import "protocol_" + service.protocol + ".html" as protocol_macros %}
 
 {% block title %}{{ service.name }}{% endblock %}
 {% block subtitle %}{% trans %}service details{% endtrans %}{% endblock %}
@@ -16,15 +15,30 @@
           <dt>{% trans %}Description{% endtrans %}</dt>
           <dd>{{ service.description }}</dd>
 
+          <dt>{% trans %}Provider{% endtrans %}</dt>
+          <dd>{{ service.provider }}</dd>
+
+          <dt>{% trans %}Application{% endtrans %}</dt>
+          <dd>{{ application.name }}</dd>
+
+          <dt>{% trans %}Application destriction{% endtrans %}</dt>
+          <dd>{{ application.__doc__ }}</dd>
+
           <dt>{% trans %}UUID{% endtrans %}</dt>
           <dd><pre>{{ service.uuid }}</pre></dd>
-
-          {{ protocol_macros.describe(service) }}
         </dl>
       </div>
       </div>
   </div>
 </div>
+<div class="row">
+  <div class="col-xs-12">
+    <div class="box">
+      <div class="box-body">
+        {{ application.render_details(service) | safe }}
+      </div>
+  </div>
+</div>
 {% endblock %}
 
 {% block actions %}
diff --git a/hiboo/service/templates/service_list.html b/hiboo/service/templates/service_list.html
index 6aa928365840b034c3fe9036279fae2a665ae686..d6bc62ba15647fcbf341a2c5765968d0681d6eb4 100644
--- a/hiboo/service/templates/service_list.html
+++ b/hiboo/service/templates/service_list.html
@@ -12,7 +12,7 @@
           <tr>
             <th>{% trans %}Service{% endtrans %}</th>
             <th>{% trans %}Provider{% endtrans %}</th>
-            <th>{% trans %}Type{% endtrans %}</th>
+            <th>{% trans %}Application{% endtrans %}</th>
             <th>{% trans %}Policy{% endtrans %}</th>
             <th>{% trans %}Max profiles{% endtrans %}</th>
             <th>{% trans %}Actions{% endtrans %}</th>
@@ -21,11 +21,10 @@
           <tr>
             <td><a href="{{ url_for(".details", service_uuid=service.uuid) }}">{{ service.name }}</a></td>
             <td>{{ service.provider }}</td>
-            <td>{{ service.protocol }}</td>
-            <td>{{ service.policy }}</td>
+            <td>{{ service.application_id }}</td>
+            <td>{{ service.POLICIES[service.policy] }}</td>
             <td>{{ service.max_profiles }}</td>
             <td>
-              <a href="{{ url_for(".details", service_uuid=service.uuid)}}">Details</a>
               <a href="{{ url_for("profile.list_for_service", service_uuid=service.uuid)}}">Profiles</a>
               <a href="{{ url_for(".edit", service_uuid=service.uuid)}}">Edit</a>
             </td>
diff --git a/hiboo/sso/__init__.py b/hiboo/sso/__init__.py
index 504ae9128498376348dfd4d28738825113730714..8e7d3f21e3c8539623ba8b49fc076006ee983aad 100644
--- a/hiboo/sso/__init__.py
+++ b/hiboo/sso/__init__.py
@@ -1,11 +1,18 @@
+from hiboo import models, application
+
 import flask
 
 
 blueprint = flask.Blueprint("sso", __name__, template_folder="templates")
 
-from hiboo.sso import saml, oidc
 
-protocols = {
-    "saml": saml,
-    "oidc": oidc
-}
+def get_service(service_uuid, expected_protocol):
+    """ Get a service by uuid, while checking for the application sso protocol
+    """
+    service = models.Service.query.get(service_uuid) or flask.abort(404)
+    app = application.registry.get(service.application_id) or flask.abort(404)
+    app.sso_protocol == expected_protocol or flask.abort(404)
+    return service
+
+
+from hiboo.sso import saml, oidc
diff --git a/hiboo/sso/forms.py b/hiboo/sso/forms.py
deleted file mode 100644
index 98a7994af18ee7df0b1f63ac47a881f0cc6d95c1..0000000000000000000000000000000000000000
--- a/hiboo/sso/forms.py
+++ /dev/null
@@ -1,48 +0,0 @@
-from wtforms import validators, fields, widgets
-from flask_babel import lazy_gettext as _
-
-import flask_wtf
-
-
-class SAMLForm(flask_wtf.FlaskForm):
-    entityid = fields.StringField(_('SP entity id'), [validators.URL(require_tld=False)])
-    acs = fields.StringField(_('SP ACS'), [validators.URL(require_tld=False)])
-    sign_mode = fields.SelectField(
-        _('Signature mode'), choices=[
-            ('response', _('Sign the full response')),
-            ('assertion', _('Sign only the assertion'))
-        ]
-    )
-    submit = fields.SubmitField(_('Submit'))
-
-
-class OIDCForm(flask_wtf.FlaskForm):
-    redirect_uri = fields.StringField(_("Redirect URI"), [validators.URL(require_tld=False)])
-    token_endpoint_auth_method = fields.SelectField(
-        _('Token Endpoint Auth Method'), choices=[
-            ("client_secret_post", _("HTTP POST data")),
-            ("client_secret_basic", _("HTTP basic authorization")),
-            ("none", _("No authentication"))
-        ]
-    )
-    grant_types = fields.SelectMultipleField(
-        _('OpenID Connect grant type'), choices=[
-            ("authorization_code", _("Authorization Code")),
-            ("implicit", _("Implicit")),
-            ("hybrid", _("Hybrid"))
-        ]
-    )
-    response_types = fields.SelectMultipleField(
-        _('Allowed response types'), choices=[
-            ("code", _("Authorization code only")),
-            ("id_token", _("Id token only")),
-            ("id_token token", _("Id token and token"))
-        ]
-    )
-    special_mappings = fields.SelectMultipleField(
-        _('Enabled special claim mappings'), choices=[
-            ("mask_sub_uuid", _("Mask the profile uuid")),
-            ("original_email", _("Return the actual user email"))
-        ]
-    )
-    submit = fields.SubmitField(_('Submit'))
diff --git a/hiboo/sso/oidc.py b/hiboo/sso/oidc.py
index 55253ad63c45e8bce8ea9c2d1cf5d3b8ada85428..9a08ce1f549a57f65983d4d477659f497faba62a 100644
--- a/hiboo/sso/oidc.py
+++ b/hiboo/sso/oidc.py
@@ -9,7 +9,7 @@ from authlib.oauth2 import rfc6749 as oauth2
 from authlib.oidc import core as oidc
 from authlib.common import security
 
-from hiboo.sso import forms, blueprint
+from hiboo.sso import blueprint, get_service
 from hiboo import models, utils, profile
 
 import flask
@@ -17,54 +17,17 @@ import time
 import inspect
 
 
-class Config(object):
-    """ Handles service configuration and forms.
-
-    Settings are:
-     - token_endpoint_auth_method: the method for authenticating clients
-     - redirect_url: the (single) supported redirect uri for the client
-     - grant_types: supported grant types
-     - response_types: supported response types (the order matters)
+def fill_service(service):
+    """ If necessary, prepare the client with cryptographic material.
     """
-
-    @classmethod
-    def derive_form(cls, form):
-        return type('DerivedOIDCForm', (forms.OIDCForm, form), {})
-
-    @classmethod
-    def populate_service(cls, form, service):
-        service.config.update({
-            "token_endpoint_auth_method": form.token_endpoint_auth_method.data,
-            "redirect_uris": [form.redirect_uri.data],
-            "grant_types": form.grant_types.data,
-            "response_types": form.response_types.data,
-            "special_mappings": form.special_mappings.data
-        })
-        cls.update_client(service)
-
-    @classmethod
-    def populate_form(cls, service, form):
-        form.process(
-            obj=service,
-            token_endpoint_auth_method=service.config.get("token_endpoint_auth_method"),
-            redirect_uri=service.config.get("redirect_uris", [""])[0],
-            grant_types=service.config.get("grant_types", ["authorization_code"]),
-            response_types=service.config.get("response_types", ["code"]),
-            special_mappings=service.config.get("special_mappings", [])
+    if "client_id" not in service.config:
+        service.config.update(
+            client_id=security.generate_token(24),
+            client_secret=security.generate_token(48),
+            jwt_key=security.generate_token(24),
+            jwt_alg="HS256"
         )
 
-    @classmethod
-    def update_client(cls, service):
-        """ If necessary, prepare the client with cryptographic material.
-        """
-        if "client_id" not in service.config:
-            service.config.update(
-                client_id=security.generate_token(24),
-                client_secret=security.generate_token(48),
-                jwt_key=security.generate_token(24),
-                jwt_alg="HS256"
-            )
-
 
 class AuthorizationCodeMixin(object):
     """ Mixin for defining oauth grants
@@ -146,12 +109,6 @@ class Client(sqla_oauth2.OAuth2ClientMixin):
         self.authorization.register_grant(Client.ImplicitGrant)
         self.authorization.register_grant(Client.HybridGrant)
 
-    @classmethod
-    def get_by_service(cls, service_uuid):
-        service = models.Service.query.get(service_uuid)
-        if service and service.protocol == "oidc":
-            return cls(service)
-
     def query_client(self, client_id):
         return self if client_id == self.client_id else None
     
@@ -208,20 +165,20 @@ class Client(sqla_oauth2.OAuth2ClientMixin):
 
 @blueprint.route("/oidc/authorize/<service_uuid>", methods=["GET", "POST"])
 def oidc_authorize(service_uuid):
-    client = Client.get_by_service(service_uuid) or flask.abort(404)
+    client = Client(get_service(service_uuid, "oidc"))
     picked = profile.get_profile(client.service, intent=True) or flask.abort(403)
     return client.authorization.create_authorization_response(grant_user=picked)
 
 
 @blueprint.route("/oidc/token/<service_uuid>", methods=["POST"])
 def oidc_token(service_uuid):
-    client = Client.get_by_service(service_uuid) or flask.abort(404)
+    client = Client(get_service(service_uuid, "oidc"))
     return client.authorization.create_token_response()
 
 
 @blueprint.route("/oidc/userinfo/<service_uuid>", methods=["GET", "POST"])
 def oidc_userinfo(service_uuid):
-    client = Client.get_by_service(service_uuid) or flask.abort(404)
+    client = Client(get_service(service_uuid, "oidc"))
     token = client.validate_token(flask.request)
     profile = models.Profile.query.get(token["profile_uuid"])
     return client.generate_user_info(profile, token["scope"])