Implementing Single Sign On…

So when I set out to rewrite our ASM system, I knew I had to find some selling points to the architecture I was pitching. One of them was the ability to write an SSO system that was easy and efficient and most of all did not require much code for implementing applications (JSSO and the like were out of the question).

So here’s what I came up with (everything is written in Grails)… I first created an AuthController which would handle the authentication and return back a user id for the person if they were a valid user (either in our custom user store or our AD system). This controller had a login action which displayed the login prompt and would return all the necessary error back to the user if something was wrong.

I then wrote another action whose corresponding GSP stored my javascript for creating the needed iframes on the page. This meant that when someone loaded the action I could access all of their cookies (which I needed for SSO) even if their application was on another domain. In the action I would process the cookies and either send them the login page or return the user id back to the browser which would then be processed by the application.

In the end the only code the implementing applications need to have is the follow:

1
2
3
4
5
6
7
8
9
10
11
12
  <script src="http://www.google.com/jsapi"></script>
  <script src="http://localhost:8080/AppHub/auth/script"></script>
  <script>
    // Load jQuery
    google.load("jquery", "1.3.2");
    google.load("jqueryui", "1.7.2");
    google.setOnLoadCallback(function() {
      loadModal('local',
      window.location.protocol+"//"+window.location.host+"/LoginApplication/login/show/",
      window.location.protocol+"//"+window.location.host+window.location.pathname);
    });
  </script>

And here is how I process the script action:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def script = {
    def cookie = request.cookies.find { it.name == 'session_id' }
    if (!cookie) {
      render(view: "script", contentType: "text/javascript", model: [flag: 'false'])
    } else {
      def id = Session.findBySessionIdAndKey(cookie.value, "id")
      if (id && cookie.value == id.sessionId) {
        render(view: "script", contentType: "text/javascript", model: [flag: 'true', id: id.value])
      } else {
        cookie.maxAge = 0
        response.addCookie(cookie)
        render(view: "script", contentType: "text/javascript", model: [flag: 'false'])
      }
    }
  }

And here is the script.gsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
function loadModal(env, targetUrl, returnUrl) {
  var flag = ${flag};
  $(window).load(function() {
    if(flag == false) {
      if(env == 'dev' || env == 'development') {
          $("head").append("<link rel=\"stylesheet\" href=\"http://162.143.99.200:8080/AppHub/css/ui-darkness/jquery-ui-1.7.2.custom.css\" type=\"text/css\" media=\"screen\" />");
          $("head").append("<link rel=\"stylesheet\" href=\"http://162.143.99.200:8080/AppHub/css/login.css\" type=\"text/css\" media=\"screen\" />");
          $("head").append("<link rel=\"stylesheet\" href=\"http://162.143.99.200:8080/AppHub/css/form.css\" type=\"text/css\" media=\"screen\" />");
          $("body").append("<div id=\"modal\"></div>");
          $("#modal").append("<div id=\"box\"></div>");
          $("#box").append("<h2 style=\"color:#666666;\">AppHub:<span class=\"light\">Connect</span></h2>");
          if(hasError('user.username.invalid')) {
            $("#box").append("<p class=\"class\" style=\"color:#f00;\"><strong>Error:</strong><g:message code="user.username.invalid" /></p>");
          }
          if(hasError('user.username.blank')) {
            $("#box").append("<p class=\"class\" style=\"color:#f00;\"><strong>Error:</strong><g:message code="user.username.blank" /></p>");
          }
          if(hasError('user.password.blank')) {
            $("#box").append("<p class=\"class\" style=\"color:#f00;\"><strong>Error:</strong><g:message code="user.password.blank" /></p>");
          }
          if(hasError('user.password.invalid')) {
            $("#box").append("<p class=\"class\" style=\"color:#f00;\"><strong>Error:</strong><g:message code="user.password.invalid" /></p>");
          }
          if(hasError('user.adproblem')) {
            $("#box").append("<p class=\"class\" style=\"color:#f00;\"><strong>Error:</strong><g:message code="user.adproblem" /></p>");
          }
          if(hasError('user.kerberos.6')) {
            $("#box").append("<p class=\"class\" style=\"color:#f00;\"><strong>Error:</strong><g:message code="user.kerberos.6" /></p>");
          }
          if(hasError('user.kerberos.18')) {
            $("#box").append("<p class=\"class\" style=\"color:#f00;\"><strong>Error:</strong><g:message code="user.kerberos.18" /></p>");
          }
          if(hasError('user.kerberos.23')) {
            $("#box").append("<p class=\"class\" style=\"color:#f00;\"><strong>Error:</strong><g:message code="user.kerberos.23" /></p>");
          }
          if(hasError('user.kerberos.24')) {
            $("#box").append("<p class=\"class\" style=\"color:#f00;\"><strong>Error:</strong><g:message code="user.kerberos.24" /></p>");
          }
          $("#box").append("<form id=\"form\" method=\"POST\" action=\"http://162.143.99.200:8080/AppHub/auth/signIn\"></form>");
          $("#form").append("<input type=\"hidden\" name=\"targetUrl\" value=\""+targetUrl+"\" />");
          $("#form").append("<input type=\"hidden\" name=\"returnUrl\" value=\""+returnUrl+"\" />");
          $("#form").append("<input type=\"hidden\" name=\"ip\" value=\"${request.getRemoteAddr()}\" />");
          $("#form").append("<ul></ul>");
          $("#form > ul").append("<li><label for=\"username\">Username</label><div><input type=\"text\" name=\"username\" class=\"max text\" /></div></li>");
          $("#form > ul").append("<li><label for=\"password\">Password</label><div><input type=\"password\" name=\"password\" class=\"max text\" /></div></li>");
          $("#form > ul").append("<li><input type=\"submit\" name=\"submit\" class=\"button\" value=\"Sign In\" /></li>");
          $("#form > ul").append("<li><a style=\"color:#0088CC;\" href=\"#\">Forgot Password</a><span style=\"color:#0088CC;\"> | </span><a style=\"color:#0088CC;\" href=\"#\">Register</a><span style=\"color:#0088CC;\"> | </span><a style=\"color:#0088CC;\" href=\"#\">Unlock Account</a></li>");
      }

      if(env == 'local') {
          $("head").append("<link rel=\"stylesheet\" href=\"http://localhost:8080/AppHub/css/ui-darkness/jquery-ui-1.7.2.custom.css\" type=\"text/css\" media=\"screen\" />");
          $("head").append("<link rel=\"stylesheet\" href=\"http://localhost:8080/AppHub/css/login.css\" type=\"text/css\" media=\"screen\" />");
          $("head").append("<link rel=\"stylesheet\" href=\"http://localhost:8080/AppHub/css/form.css\" type=\"text/css\" media=\"screen\" />");
          $("body").append("<div id=\"modal\"></div>");
          $("#modal").append("<div id=\"box\"></div>");
          $("#box").append("<h2 style=\"color:#666666;\">AppHub:<span class=\"light\">Connect</span></h2>")
          if(hasError('user.username.invalid')) {
            $("#box").append("<p class=\"class\" style=\"color:#f00;\"><strong>Error:</strong><g:message code="user.username.invalid" /></p>");
          }
          if(hasError('user.username.blank')) {
            $("#box").append("<p class=\"class\" style=\"color:#f00;\"><strong>Error:</strong><g:message code="user.username.blank" /></p>");
          }
          if(hasError('user.password.blank')) {
            $("#box").append("<p class=\"class\" style=\"color:#f00;\"><strong>Error:</strong><g:message code="user.password.blank" /></p>");
          }
          if(hasError('user.password.invalid')) {
            $("#box").append("<p class=\"class\" style=\"color:#f00;\"><strong>Error:</strong><g:message code="user.password.invalid" /></p>");
          }
          if(hasError('user.adproblem')) {
            $("#box").append("<p class=\"class\" style=\"color:#f00;\"><strong>Error:</strong><g:message code="user.adproblem" /></p>");
          }
          if(hasError('user.kerberos.6')) {
            $("#box").append("<p class=\"class\" style=\"color:#f00;\"><strong>Error:</strong><g:message code="user.kerberos.6" /></p>");
          }
          if(hasError('user.kerberos.18')) {
            $("#box").append("<p class=\"class\" style=\"color:#f00;\"><strong>Error:</strong><g:message code="user.kerberos.18" /></p>");
          }
          if(hasError('user.kerberos.23')) {
            $("#box").append("<p class=\"class\" style=\"color:#f00;\"><strong>Error:</strong><g:message code="user.kerberos.23" /></p>");
          }
          if(hasError('user.kerberos.24')) {
            $("#box").append("<p class=\"class\" style=\"color:#f00;\"><strong>Error:</strong><g:message code="user.kerberos.24" /></p>");
          }
          $("#box").append("<form id=\"form\" method=\"POST\" action=\"http://localhost:8080/AppHub/auth/signIn\"></form>");
          $("#form").append("<input type=\"hidden\" name=\"targetUrl\" value=\""+targetUrl+"\" />");
          $("#form").append("<input type=\"hidden\" name=\"returnUrl\" value=\""+returnUrl+"\" />");
          $("#form").append("<input type=\"hidden\" name=\"ip\" value=\"${request.getRemoteAddr()}\" />");
          $("#form").append("<ul></ul>");
          $("#form > ul").append("<li><label for=\"username\">Username</label><div><input type=\"text\" name=\"username\" class=\"max text\" /></div></li>");
          $("#form > ul").append("<li><label for=\"password\">Password</label><div><input type=\"password\" name=\"password\" class=\"max text\" /></div></li>");
          $("#form > ul").append("<li><input type=\"submit\" name=\"submit\" class=\"button\" value=\"Sign In\" /></li>");
          $("#form > ul").append("<li><a style=\"color:#0088CC;\" href=\"#\">Forgot Password</a><span style=\"color:#0088CC;\"> | </span><a style=\"color:#0088CC;\" href=\"#\">Register</a><span style=\"color:#0088CC;\"> | </span><a style=\"color:#0088CC;\" href=\"#\">Unlock Account</a></li>");
      }

      //open dialog
      $("#modal").dialog({
          autoOpen: true,
          height: 450,
          width: 500,
          modal: true,
          resizable: false,
          position: 'center',
          draggable: false,
          closeOnEscape: false
      });
      //hide the title bar
      $(".ui-dialog-titlebar").hide();
    } else {
      $("body").append("<form id=\"form\" method=\"POST\" action=\""+targetUrl+"\"></form>");
      $("#form").append("<input type=\"hidden\" name=\"id\" value=\"${id}\" />");
      $("#form").append("<input type=\"hidden\" name=\"ip\" value=\"${request.getRemoteAddr()}\" />");
      $("#form").submit();
    }
  });
}

function getParameters() {
    var vars = [], hash;
    var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
    for(var i = 0; i < hashes.length; i++) {
        hash = hashes[i].split('=');
        vars.push(hash[0]);
        vars[hash[0]] = hash[1];
    }
    return vars;
}

function getParameter(name) {
  return getParameters()[name];
}

function getErrors() {
  if(getParameter('error') != null) {
    return getParameter('error').split(';');
  } else {
    return null
  }
}

function hasError(code) {
  var errors = getErrors();
  if(getErrors() != null) {
    for(var i = 0; i < getErrors().length; i++) {
      if(errors[i] == code) {
        return true;
      }
    }
  } else {
    return false;
  }
}

Could it be cleaner? Sure and when I hone in my JQuery skills I will definitely clean it up, but for now it works. =)

Posted in: REST, grails, javascript



This website uses IntenseDebate comments, but they are not currently loaded because either your browser doesn't support JavaScript, or they didn't load fast enough.

addLeave a comment

You must be logged in to post a comment.