keycloak-aplcache

Added audit to admin console

4/4/2014 10:23:14 AM

Details

diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js
index d780e60..6fa9473 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js
@@ -131,6 +131,15 @@ module.config([ '$routeProvider', function($routeProvider) {
             },
             controller : 'RealmLdapSettingsCtrl'
         })
+        .when('/realms/:realm/audit', {
+            templateUrl : 'partials/realm-audit.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                }
+            },
+            controller : 'RealmAuditCtrl'
+        })
         .when('/create/user/:realm', {
             templateUrl : 'partials/user-detail.html',
             resolve : {
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js
index b441e31..c2e78cd 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js
@@ -42,6 +42,10 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location
                 return getAccess('view-users') || this.manageClients;
             },
 
+            get viewAudit() {
+                return getAccess('view-audit') || this.manageClients;
+            },
+
             get manageRealm() {
                 return getAccess('manage-realm');
             },
@@ -56,6 +60,10 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location
 
             get manageUsers() {
                 return getAccess('manage-users');
+            },
+
+            get manageAudit() {
+                return getAccess('manage-audit');
             }
         }
     })
@@ -914,4 +922,49 @@ module.controller('RealmLdapSettingsCtrl', function($scope, Realm, realm, $locat
         $scope.realm = angular.copy(oldCopy);
         $scope.changed = false;
     };
+});
+
+
+module.controller('RealmAuditCtrl', function($scope, RealmAudit, realm) {
+    $scope.realm = realm;
+    $scope.page = 0;
+
+    $scope.query = {
+        id : realm.realm,
+        max : 5,
+        first : 0
+    }
+
+    $scope.update = function() {
+        for (var i in $scope.query) {
+            if ($scope.query[i] === '') {
+                delete $scope.query[i];
+           }
+        }
+        console.debug($scope.query.first);
+        $scope.events = RealmAudit.query($scope.query);
+    }
+
+    $scope.firstPage = function() {
+        $scope.query.first = 0;
+        if ($scope.query.first < 0) {
+            $scope.query.first = 0;
+        }
+        $scope.update();
+    }
+
+    $scope.previousPage = function() {
+        $scope.query.first -= parseInt($scope.query.max);
+        if ($scope.query.first < 0) {
+            $scope.query.first = 0;
+        }
+        $scope.update();
+    }
+
+    $scope.nextPage = function() {
+        $scope.query.first += parseInt($scope.query.max);
+        $scope.update();
+    }
+
+    $scope.update();
 });
\ No newline at end of file
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js
index cb31b5e..4d904d6 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js
@@ -142,6 +142,12 @@ module.factory('Realm', function($resource) {
     });
 });
 
+module.factory('RealmAudit', function($resource) {
+    return $resource('/auth/rest/admin/realms/:id/audit', {
+        id : '@realm'
+    });
+});
+
 module.factory('ServerInfo', function($resource) {
     return $resource('/auth/rest/admin/serverinfo');
 });
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-audit.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-audit.html
new file mode 100755
index 0000000..f31ab36
--- /dev/null
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-audit.html
@@ -0,0 +1,100 @@
+<div class="bs-sidebar col-sm-3 " data-ng-include data-src="'partials/realm-menu.html'"></div>
+<div id="content-area" class="col-sm-9" role="main">
+    <data-kc-navigation data-kc-current="social" data-kc-realm="realm.realm" data-kc-social="realm.social"></data-kc-navigation>
+    <div id="content">
+        <ol class="breadcrumb">
+            <li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
+            <li><a href="#/realms/{{realm.realm}}">Audit</a></li>
+            <li class="active">Social</li>
+        </ol>
+        <h2><span>{{realm.realm}}</span> Audit Log</h2>
+
+        <table class="table">
+            <thead>
+            <tr>
+                <th class="kc-table-actions" colspan="4">
+                    <div class="pull-right">
+                        <select data-ng-model="query.max" data-ng-click="update()" class="btn btn-default">
+                            <option>5</option>
+                            <option>10</option>
+                            <option>50</option>
+                            <option>100</option>
+                        </select>
+                        <button class="btn btn-default" data-ng-click="filter = !filter">
+                            <span class="glyphicon glyphicon-plus" data-ng-show="!filter"></span>
+                            <span class="glyphicon glyphicon-minus" data-ng-show="filter"></span>
+                            Filter
+                        </button>
+                        <button class="btn btn-default btn-primary" data-ng-click="update()">Update</button>
+                    </div>
+                    <form class="form-horizontal">
+                        <div class="form-group" data-ng-show="filter">
+                            <label class="col-sm-2 control-label" for="event">Event</label>
+                            <div class="col-sm-4">
+                                <input class="form-control" type="text" id="event" name="event" data-ng-model="query.event">
+                            </div>
+                        </div>
+                        <div class="form-group" data-ng-show="filter">
+                            <label class="col-sm-2 control-label" for="client">Client</label>
+                            <div class="col-sm-4">
+                                <input class="form-control" type="text" id="client" name="client" data-ng-model="query.client">
+                            </div>
+                        </div>
+                        <div class="form-group" data-ng-show="filter">
+                            <label class="col-sm-2 control-label" for="user">User</label>
+                            <div class="col-sm-4">
+                                <input class="form-control" type="text" id="user" name="user" data-ng-model="query.user">
+                            </div>
+                        </div>
+                    </form>
+                </th>
+            </tr>
+            <tr>
+                <th width="100px">Time</th>
+                <th width="180px">Event</th>
+                <th>Details</th>
+            </tr>
+            </thead>
+            <tfoot>
+            <tr>
+                <td colspan="7">
+                    <div class="table-nav">
+                        <button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">First page</button>
+                        <button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">Previous page</button>
+                        <button data-ng-click="nextPage()" class="next" ng-disabled="events.length < query.max">Next page</button>
+                    </div>
+                </td>
+            </tr>
+            </tfoot>
+            <tbody>
+                <tr ng-repeat="event in events">
+                    <td>{{event.time|date:'shortDate'}}<br>{{event.time|date:'mediumTime'}}</td>
+                    <td data-ng-class="event.error && 'audit-error' || 'audit-success'">{{event.event}}<br/>{{event.error}}</td>
+                    <td>
+                        <table>
+                            <tr><td width="100px">Client</td><td>{{event.clientId}}</td></tr>
+                            <tr><td>User</td><td>{{event.userId}}</td></tr>
+                            <tr><td>IP Address</td><td>{{event.ipAddress}}</td></tr>
+                            <tr>
+                                <td>Details</td>
+                                <td>
+                                    <button type="button" class="btn btn-default btn-xs" ng-click="event.collapse = !event.collapse">
+                                        <span class="glyphicon glyphicon-plus" data-ng-show="!event.collapse"></span>
+                                        <span class="glyphicon glyphicon-minus" data-ng-show="event.collapse"></span>
+                                    </button>
+                                    <table data-ng-show="event.collapse">
+                                        <tr ng-repeat="(key, value) in event.details">
+                                            <td>{{key}}</td>
+                                            <td>{{value}}</td>
+                                        </tr>
+                                    </table>
+                                </td>
+                            </tr>
+                        </table>
+                    </td>
+                </tr>
+            </tbody>
+        </table>
+    </div>
+</div>
+</div>
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-menu.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-menu.html
index b0ae386..e2d0a2b 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-menu.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-menu.html
@@ -7,4 +7,5 @@
     <li data-ng-show="access.viewApplications" data-ng-class="(path[2] == 'applications' || path[1] == 'application' || path[3] == 'applications') && 'active'"><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
     <li data-ng-show="access.viewClients" data-ng-class="(path[2] == 'oauth-clients' || path[1] == 'oauth-client') && 'active'"><a href="#/realms/{{realm.realm}}/oauth-clients">OAuth Clients</a></li>
     <li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'sessions') && 'active'"><a href="#/realms/{{realm.realm}}/sessions/revocation">Sessions</a></li>
+    <li data-ng-show="access.viewAudit" data-ng-class="(path[2] == 'audit') && 'active'"><a href="#/realms/{{realm.realm}}/audit">Audit</a></li>
 </ul>
\ No newline at end of file
diff --git a/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/tables.css b/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/tables.css
index 0e60a40..4ae1b27 100644
--- a/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/tables.css
+++ b/admin-ui-styles/src/main/resources/META-INF/resources/admin-ui/css/tables.css
@@ -11,6 +11,9 @@ table caption {
 table tbody tr:nth-child(even) {
   background-color: #f6f6f6;
 }
+table tbody tr:nth-child(odd) {
+  background-color: #fff;
+}
 table tbody tr td,
 table thead tr th {
   font-weight: normal;
@@ -138,10 +141,14 @@ table tfoot tr {
 table tfoot tr .table-nav {
   float: right;
 }
-table tfoot tr .table-nav a {
+table tfoot tr .table-nav a,
+table tfoot tr .table-nav button {
   display: inline-block;
-  line-height: 2.4em;
+  line-height: 22px;
   border-left: 1px solid #d9d9d9;
+  border-right: none;
+  border-top: none;
+  border-bottom: none;
   width: 3.5em;
   background-color: #f3f3f3;
   background-image: linear-gradient(top, #fafafa 0%, #ededed 100%);
@@ -156,28 +163,35 @@ table tfoot tr .table-nav a {
   background-position: left top;
   vertical-align: top;
 }
-table tfoot tr .table-nav a.last {
+table tfoot tr .table-nav a.last,
+table tfoot tr .table-nav button.last {
   background-position: top right;
 }
-table tfoot tr .table-nav a.prev {
+table tfoot tr .table-nav a.prev,
+table tfoot tr .table-nav button.prev {
   background-position: bottom left;
 }
-table tfoot tr .table-nav a.next {
+table tfoot tr .table-nav a.next,
+table tfoot tr .table-nav button.next {
   background-position: bottom right;
 }
-table tfoot tr .table-nav a:hover {
+table tfoot tr .table-nav a:hover,
+table tfoot tr .table-nav button:hover {
   background-image: url(img/sprite-table-nav.png);
   background-color: #eeeeee;
 }
-table tfoot tr .table-nav a:active {
+table tfoot tr .table-nav a:active,
+table tfoot tr .table-nav button:active {
   box-shadow: 0 0 5px 2px rgba(0, 0, 0, 0.25) inset;
 }
-table tfoot tr .table-nav a.disabled {
+table tfoot tr .table-nav a.disabled,
+table tfoot tr .table-nav button:disabled {
   opacity: 0.5;
   filter: alpha(opacity=50);
   cursor: default;
 }
-table tfoot tr .table-nav a.disabled:active {
+table tfoot tr .table-nav a.disabled:active,
+table tfoot tr .table-nav button:disabled:active {
   box-shadow: none;
 }
 table tfoot tr .table-nav span {
@@ -195,3 +209,20 @@ table tfoot tr .table-nav span {
 td .form-group {
     margin-bottom: 0;
 }
+
+td.audit-success {
+    background-color: #E4F1E1;
+}
+
+td.audit-error {
+    background-color: #F8E7E7;
+}
+
+.kc-table-actions .form-group {
+    margin-top: 5px;
+    margin-bottom: 5px;
+}
+
+.kc-table-actions select {
+    height: 26px;
+}
\ No newline at end of file
diff --git a/audit/api/src/main/java/org/keycloak/audit/Event.java b/audit/api/src/main/java/org/keycloak/audit/Event.java
index 85b968a..6ab7400 100644
--- a/audit/api/src/main/java/org/keycloak/audit/Event.java
+++ b/audit/api/src/main/java/org/keycloak/audit/Event.java
@@ -72,10 +72,6 @@ public class Event {
         this.ipAddress = ipAddress;
     }
 
-    public boolean isError() {
-        return error != null;
-    }
-
     public String getError() {
         return error;
     }
diff --git a/audit/api/src/main/java/org/keycloak/audit/EventQuery.java b/audit/api/src/main/java/org/keycloak/audit/EventQuery.java
index 339bc64..3e5cf60 100644
--- a/audit/api/src/main/java/org/keycloak/audit/EventQuery.java
+++ b/audit/api/src/main/java/org/keycloak/audit/EventQuery.java
@@ -15,6 +15,8 @@ public interface EventQuery {
 
     public EventQuery user(String userId);
 
+    public EventQuery ipAddress(String ipAddress);
+
     public EventQuery firstResult(int result);
 
     public EventQuery maxResults(int results);
diff --git a/audit/jboss-logging/src/main/java/org/keycloak/audit/log/JBossLoggingAuditListener.java b/audit/jboss-logging/src/main/java/org/keycloak/audit/log/JBossLoggingAuditListener.java
index d5381e2..691a322 100644
--- a/audit/jboss-logging/src/main/java/org/keycloak/audit/log/JBossLoggingAuditListener.java
+++ b/audit/jboss-logging/src/main/java/org/keycloak/audit/log/JBossLoggingAuditListener.java
@@ -19,7 +19,7 @@ public class JBossLoggingAuditListener implements AuditListener {
 
     @Override
     public void onEvent(Event event) {
-        Logger.Level level = event.isError() ? Logger.Level.WARN : Logger.Level.INFO;
+        Logger.Level level = event.getError() != null ? Logger.Level.WARN : Logger.Level.INFO;
 
         if (logger.isEnabled(level)) {
             StringBuilder sb = new StringBuilder();
@@ -35,7 +35,7 @@ public class JBossLoggingAuditListener implements AuditListener {
             sb.append(", ipAddress=");
             sb.append(event.getIpAddress());
 
-            if (event.isError()) {
+            if (event.getError() != null) {
                 sb.append(", error=");
                 sb.append(event.getError());
             }
diff --git a/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaEventQuery.java b/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaEventQuery.java
index c0dd127..f69fe4b 100644
--- a/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaEventQuery.java
+++ b/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaEventQuery.java
@@ -60,6 +60,12 @@ public class JpaEventQuery implements EventQuery {
     }
 
     @Override
+    public EventQuery ipAddress(String ipAddress) {
+        predicates.add(cb.equal(root.get("ipAddress"), ipAddress));
+        return this;
+    }
+
+    @Override
     public EventQuery firstResult(int firstResult) {
         this.firstResult = firstResult;
         return this;
diff --git a/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoEventQuery.java b/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoEventQuery.java
index 5c75e63..d220f0e 100644
--- a/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoEventQuery.java
+++ b/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoEventQuery.java
@@ -49,6 +49,12 @@ public class MongoEventQuery implements EventQuery {
     }
 
     @Override
+    public EventQuery ipAddress(String ipAddress) {
+        query.put("ipAddress", ipAddress);
+        return this;
+    }
+
+    @Override
     public EventQuery firstResult(int firstResult) {
         this.firstResult = firstResult;
         return this;
diff --git a/model/api/src/main/java/org/keycloak/models/AdminRoles.java b/model/api/src/main/java/org/keycloak/models/AdminRoles.java
index e1216cf..e21a57a 100644
--- a/model/api/src/main/java/org/keycloak/models/AdminRoles.java
+++ b/model/api/src/main/java/org/keycloak/models/AdminRoles.java
@@ -15,13 +15,15 @@ public class AdminRoles {
     public static String VIEW_USERS = "view-users";
     public static String VIEW_APPLICATIONS = "view-applications";
     public static String VIEW_CLIENTS = "view-clients";
+    public static String VIEW_AUDIT = "view-audit";
 
     public static String MANAGE_REALM = "manage-realm";
     public static String MANAGE_USERS = "manage-users";
     public static String MANAGE_APPLICATIONS = "manage-applications";
     public static String MANAGE_CLIENTS = "manage-clients";
+    public static String MANAGE_AUDIT = "manage-audit";
 
-    public static String[] ALL_REALM_ROLES = {VIEW_REALM, VIEW_USERS, VIEW_APPLICATIONS, VIEW_CLIENTS, MANAGE_REALM, MANAGE_USERS, MANAGE_APPLICATIONS, MANAGE_CLIENTS};
+    public static String[] ALL_REALM_ROLES = {VIEW_REALM, VIEW_USERS, VIEW_APPLICATIONS, VIEW_CLIENTS, VIEW_AUDIT, MANAGE_REALM, MANAGE_USERS, MANAGE_APPLICATIONS, MANAGE_CLIENTS, MANAGE_AUDIT};
 
     public static String getAdminApp(RealmModel realm) {
         return realm.getName() + APP_SUFFIX;
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index febd31c..a789660 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -95,7 +95,7 @@ public class AccountService {
     private static final Logger logger = Logger.getLogger(AccountService.class);
 
     private static final String[] AUDIT_EVENTS = {Events.LOGIN, Events.LOGOUT, Events.REGISTER, Events.REMOVE_SOCIAL_LINK, Events.REMOVE_TOTP, Events.SEND_RESET_PASSWORD,
-            Events.SEND_VERIFY_EMAIL, Events.SOCIAL_LINK, Events.UPDATE_EMAIL, Events.UPDATE_PASSWORD, Events.UPDATE_PASSWORD, Events.UPDATE_TOTP, Events.VERIFY_EMAIL};
+            Events.SEND_VERIFY_EMAIL, Events.SOCIAL_LINK, Events.UPDATE_EMAIL, Events.UPDATE_PASSWORD, Events.UPDATE_PROFILE, Events.UPDATE_TOTP, Events.VERIFY_EMAIL};
 
     private static final Set<String> AUDIT_DETAILS = new HashSet<String>();
     static {
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index c9bf4f4..4194e61 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -2,11 +2,15 @@ package org.keycloak.services.resources.admin;
 
 import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.logging.Logger;
+import org.keycloak.audit.AuditProvider;
+import org.keycloak.audit.Event;
+import org.keycloak.audit.EventQuery;
 import org.keycloak.models.ApplicationModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.representations.adapters.action.SessionStats;
 import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.ProviderSession;
 import org.keycloak.services.managers.ModelToRepresentation;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.managers.ResourceAdminManager;
@@ -36,6 +40,9 @@ public class RealmAdminResource {
     @Context
     protected KeycloakSession session;
 
+    @Context
+    protected ProviderSession providers;
+
     public RealmAdminResource(RealmAuth auth, RealmModel realm, TokenManager tokenManager) {
         this.auth = auth;
         this.realm = realm;
@@ -129,7 +136,7 @@ public class RealmAdminResource {
     @GET
     @NoCache
     @Produces(MediaType.APPLICATION_JSON)
-    public Map<String,SessionStats> getSessionStats() {
+    public Map<String, SessionStats> getSessionStats() {
         logger.info("session-stats");
         auth.requireView();
         Map<String, SessionStats> stats = new HashMap<String, SessionStats>();
@@ -141,4 +148,37 @@ public class RealmAdminResource {
         return stats;
     }
 
+    @Path("audit")
+    @GET
+    @NoCache
+    @Produces(MediaType.APPLICATION_JSON)
+    public List<Event> getAudit(@QueryParam("client") String client, @QueryParam("event") String event, @QueryParam("user") String user,
+                                @QueryParam("ipAddress") String ipAddress, @QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResults) {
+        auth.init(RealmAuth.Resource.AUDIT).requireView();
+
+        AuditProvider audit = providers.getProvider(AuditProvider.class);
+
+        EventQuery query = audit.createQuery().realm(realm.getId());
+        if (client != null) {
+            query.client(client);
+        }
+        if (event != null) {
+            query.event(event);
+        }
+        if (user != null) {
+            query.user(user);
+        }
+        if (ipAddress != null) {
+            query.ipAddress(ipAddress);
+        }
+        if (firstResult != null) {
+            query.firstResult(firstResult);
+        }
+        if (maxResults != null) {
+            query.maxResults(maxResults);
+        }
+
+        return query.getResultList();
+    }
+
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java
index d059c4c..e8b8e64 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java
@@ -13,7 +13,7 @@ public class RealmAuth {
     private Resource resource;
 
     public enum Resource {
-        APPLICATION, CLIENT, USER, REALM
+        APPLICATION, CLIENT, USER, REALM, AUDIT
     }
 
     private Auth auth;
@@ -65,6 +65,8 @@ public class RealmAuth {
                 return AdminRoles.VIEW_USERS;
             case REALM:
                 return AdminRoles.VIEW_REALM;
+            case AUDIT:
+                return AdminRoles.VIEW_AUDIT;
             default:
                 throw new IllegalStateException();
         }
@@ -80,6 +82,8 @@ public class RealmAuth {
                 return AdminRoles.MANAGE_USERS;
             case REALM:
                 return AdminRoles.MANAGE_REALM;
+            case AUDIT:
+                return AdminRoles.MANAGE_AUDIT;
             default:
                 throw new IllegalStateException();
         }
diff --git a/services/src/main/java/org/keycloak/services/resources/SocialResource.java b/services/src/main/java/org/keycloak/services/resources/SocialResource.java
index 815418a..4204ff9 100755
--- a/services/src/main/java/org/keycloak/services/resources/SocialResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/SocialResource.java
@@ -243,7 +243,7 @@ public class SocialResource {
             realm.addSocialLink(user, socialLink);
 
             audit.clone().user(user).event(Events.REGISTER)
-                    .detail(Details.REGISTER_METHOD, "social")
+                    .detail(Details.REGISTER_METHOD, "social@" + provider.getId())
                     .detail(Details.EMAIL, socialUser.getEmail())
                     .removeDetail("auth_method")
                     .success();
@@ -256,7 +256,7 @@ public class SocialResource {
             return oauth.forwardToSecurityFailure("Your account is not enabled.");
         }
 
-        return oauth.processAccessCode(scope, state, redirectUri, client, user, socialLink.getSocialUserId() + "@" + socialLink.getSocialProvider(), false, "social", audit);
+        return oauth.processAccessCode(scope, state, redirectUri, client, user, socialLink.getSocialUserId() + "@" + socialLink.getSocialProvider(), false, "social@" + provider.getId(), audit);
     }
 
     @GET
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java
index 8424550..6ed3294 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java
@@ -113,12 +113,12 @@ public class SocialLoginTest {
                 .user(AssertEvents.isUUID())
                 .detail(Details.EMAIL, "bob@builder.com")
                 .detail(Details.RESPONSE_TYPE, "code")
-                .detail(Details.REGISTER_METHOD, "social")
+                .detail(Details.REGISTER_METHOD, "social@dummy")
                 .detail(Details.REDIRECT_URI, AssertEvents.DEFAULT_REDIRECT_URI)
                 .detail(Details.USERNAME, "1@dummy")
                 .assertEvent().getUserId();
 
-        String codeId = events.expectLogin().user(userId).detail(Details.USERNAME, "1@dummy").detail(Details.AUTH_METHOD, "social").assertEvent().getDetails().get(Details.CODE_ID);
+        String codeId = events.expectLogin().user(userId).detail(Details.USERNAME, "1@dummy").detail(Details.AUTH_METHOD, "social@dummy").assertEvent().getDetails().get(Details.CODE_ID);
 
         AccessTokenResponse response = oauth.doAccessTokenRequest(oauth.getCurrentQuery().get(OAuth2Constants.CODE), "password");
 
@@ -146,7 +146,7 @@ public class SocialLoginTest {
         driver.findElement(By.id("username")).sendKeys("dummy-user1");
         driver.findElement(By.id("login")).click();
 
-        events.expectLogin().user(userId).detail(Details.USERNAME, "1@dummy").detail(Details.AUTH_METHOD, "social").assertEvent();
+        events.expectLogin().user(userId).detail(Details.USERNAME, "1@dummy").detail(Details.AUTH_METHOD, "social@dummy").assertEvent();
     }
 
     @Test
@@ -160,7 +160,7 @@ public class SocialLoginTest {
         Assert.assertTrue(loginPage.isCurrent());
         Assert.assertEquals("Access denied", loginPage.getWarning());
 
-        events.expectLogin().error("rejected_by_user").user((String) null).detail(Details.AUTH_METHOD, "social").removeDetail(Details.USERNAME).removeDetail(Details.CODE_ID).assertEvent();
+        events.expectLogin().error("rejected_by_user").user((String) null).detail(Details.AUTH_METHOD, "social@dummy").removeDetail(Details.USERNAME).removeDetail(Details.CODE_ID).assertEvent();
 
         loginPage.login("test-user@localhost", "password");
 
@@ -200,19 +200,19 @@ public class SocialLoginTest {
                     .user(AssertEvents.isUUID())
                     .detail(Details.EMAIL, "bob@builder.com")
                     .detail(Details.RESPONSE_TYPE, "code")
-                    .detail(Details.REGISTER_METHOD, "social")
+                    .detail(Details.REGISTER_METHOD, "social@dummy")
                     .detail(Details.REDIRECT_URI, AssertEvents.DEFAULT_REDIRECT_URI)
                     .detail(Details.USERNAME, "2@dummy")
                     .assertEvent().getUserId();
 
             profilePage.update("Dummy", "User", "dummy-user-reg@dummy-social");
 
-            events.expectRequiredAction("update_profile").user(userId).detail(Details.AUTH_METHOD, "social").detail(Details.USERNAME, "2@dummy").assertEvent();
-            events.expectRequiredAction("update_email").user(userId).detail(Details.AUTH_METHOD, "social").detail(Details.USERNAME, "2@dummy").detail(Details.PREVIOUS_EMAIL, "bob@builder.com").detail(Details.UPDATED_EMAIL, "dummy-user-reg@dummy-social").assertEvent();
+            events.expectRequiredAction("update_profile").user(userId).detail(Details.AUTH_METHOD, "social@dummy").detail(Details.USERNAME, "2@dummy").assertEvent();
+            events.expectRequiredAction("update_email").user(userId).detail(Details.AUTH_METHOD, "social@dummy").detail(Details.USERNAME, "2@dummy").detail(Details.PREVIOUS_EMAIL, "bob@builder.com").detail(Details.UPDATED_EMAIL, "dummy-user-reg@dummy-social").assertEvent();
 
             Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
 
-            String codeId = events.expectLogin().user(userId).removeDetail(Details.USERNAME).detail(Details.AUTH_METHOD, "social").detail(Details.USERNAME, "2@dummy").assertEvent().getDetails().get(Details.CODE_ID);
+            String codeId = events.expectLogin().user(userId).removeDetail(Details.USERNAME).detail(Details.AUTH_METHOD, "social@dummy").detail(Details.USERNAME, "2@dummy").assertEvent().getDetails().get(Details.CODE_ID);
 
             AccessTokenResponse response = oauth.doAccessTokenRequest(oauth.getCurrentQuery().get(OAuth2Constants.CODE), "password");
             AccessToken token = oauth.verifyToken(response.getAccessToken());