diff --git a/config/default.js b/config/default.js
index 632c5ca..e17d3f4 100644
--- a/config/default.js
+++ b/config/default.js
@@ -74,5 +74,10 @@ module.exports = {
   ROLE_ID_SUBMITTER: process.env.ROLE_ID_SUBMITTER || '732339e7-8e30-49d7-9198-cccf9451e221',
   TYPE_ID_TASK: process.env.TYPE_ID_TASK || 'ecd58c69-238f-43a4-a4bb-d172719b9f31',
   DEFAULT_TIMELINE_TEMPLATE_ID: process.env.DEFAULT_TIMELINE_TEMPLATE_ID || '53a307ce-b4b3-4d6f-b9a1-3741a58f77e6',
-  DEFAULT_TRACK_ID: process.env.DEFAULT_TRACK_ID || '9b6fc876-f4d9-4ccb-9dfd-419247628825'
+  DEFAULT_TRACK_ID: process.env.DEFAULT_TRACK_ID || '9b6fc876-f4d9-4ccb-9dfd-419247628825',
+  GITLAB_ACCESS_TOKEN_DEFAULT_EXPIRATION: 3600 * 2,
+  GITLAB_REFRESH_TOKEN_BEFORE_EXPIRATION: 300,
+  GITLAB_CLIENT_ID: process.env.GITLAB_CLIENT_ID,
+  GITLAB_CLIENT_SECRET: process.env.GITLAB_CLIENT_SECRET,
+  GITLAB_OWNER_USER_CALLBACK_URL: process.env.GITLAB_OWNER_USER_CALLBACK_URL
 };
diff --git a/configuration.md b/configuration.md
index 5ca0e4f..a8f5ddb 100644
--- a/configuration.md
+++ b/configuration.md
@@ -16,6 +16,9 @@ The following config parameters are supported, they are defined in `config/defau
 | NEW_CHALLENGE_DURATION_IN_DAYS | the duration of new challenge | 5 |
 |TC_URL| the base URL of topcoder to get the challenge URL| defaults to `https://www.topcoder-dev.com`|
 |GITLAB_API_BASE_URL| the URL for gitlab host| defaults to `https://gitlab.com`|
+| GITLAB_CLIENT_ID                       | the GitLab client id | No default - needs to be set up with same value found in topcoder-x-ui |
+| GITLAB_CLIENT_SECRET | the GitLab client secret | No default - needs to be set up with same value found in topcoder-x-ui |
+| GITLAB_OWNER_USER_CALLBACK_URL         | the GitLab callback redirect uri for refreshing copilot token | No default - needs to be set up with same owner user callback value in topcoder-x-ui |
 |PAID_ISSUE_LABEL|the label name for paid, should be one of the label configured in topcoder x ui|'tcx_Paid'|
 |FIX_ACCEPTED_ISSUE_LABEL|the label name for fix accepted, should be one of the label configured in topcoder x ui|'tcx_FixAccepted'|
 |ASSIGNED_ISSUE_LABEL| the label name for assigned, should be one of the label configured in topcoder x ui| 'tcx_Assigned'|
diff --git a/index.js b/index.js
index ee57023..ef15b99 100644
--- a/index.js
+++ b/index.js
@@ -22,7 +22,9 @@ process.on('unhandledRejection', (err) => {
 });
 
 // dump the configuration to logger
-const ignoreConfigLog = ['cert', 'key', 'AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AUTH0_CLIENT_ID', 'AUTH0_CLIENT_SECRET'];
+const ignoreConfigLog = ['cert', 'key', 'AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AUTH0_CLIENT_ID', 'AUTH0_CLIENT_SECRET',
+  'GITLAB_CLIENT_ID', 'GITLAB_CLIENT_SECRET'];
+
 /**
  * Print configs to logger
  * @param {Object} params the config params
diff --git a/services/GitlabService.js b/services/GitlabService.js
index 542e9bb..559b31c 100644
--- a/services/GitlabService.js
+++ b/services/GitlabService.js
@@ -16,9 +16,18 @@ const GitlabAPI = require('node-gitlab-api');
 const logger = require('../utils/logger');
 const errors = require('../utils/errors');
 const helper = require('../utils/helper');
+const dbHelper = require('../utils/db-helper');
+const superagent = require('superagent');
+const superagentPromise = require('superagent-promise');
+
+const request = superagentPromise(superagent, Promise);
+// milliseconds per second
+const MS_PER_SECOND = 1000;
 
 const copilotUserSchema = Joi.object().keys({
   accessToken: Joi.string().required(),
+  accessTokenExpiration: Joi.date().required(),
+  refreshToken: Joi.string().required(),
   userProviderId: Joi.number().required(),
   topcoderUsername: Joi.string()
 }).required();
@@ -80,7 +89,8 @@ function _getIssueUrl(repoPath, issueId) {
 async function createComment(copilot, project, issueId, body) {
   const projectId = project.id;
   Joi.attempt({copilot, projectId, issueId, body}, createComment.schema);
-  const gitlab = await _authenticate(copilot.accessToken);
+  const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
+  const gitlab = await _authenticate(refreshedCopilot.accessToken);
   try {
     body = helper.prepareAutomatedComment(body, copilot);
     await gitlab.projects.issues.notes.create(projectId, issueId, {body});
@@ -107,7 +117,8 @@ createComment.schema = {
 async function updateIssue(copilot, project, issueId, title) {
   const projectId = project.id;
   Joi.attempt({copilot, projectId, issueId, title}, updateIssue.schema);
-  const gitlab = await _authenticate(copilot.accessToken);
+  const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
+  const gitlab = await _authenticate(refreshedCopilot.accessToken);
   try {
     await gitlab.projects.issues.edit(projectId, issueId, {title});
   } catch (err) {
@@ -133,7 +144,8 @@ updateIssue.schema = {
 async function assignUser(copilot, project, issueId, userId) {
   const projectId = project.id;
   Joi.attempt({copilot, projectId, issueId, userId}, assignUser.schema);
-  const gitlab = await _authenticate(copilot.accessToken);
+  const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
+  const gitlab = await _authenticate(refreshedCopilot.accessToken);
   try {
     const issue = await gitlab.projects.issues.show(projectId, issueId);
     const oldAssignees = _.without(issue.assignee_ids, userId);
@@ -164,7 +176,8 @@ assignUser.schema = {
 async function removeAssign(copilot, project, issueId, userId) {
   const projectId = project.id;
   Joi.attempt({copilot, projectId, issueId, userId}, removeAssign.schema);
-  const gitlab = await _authenticate(copilot.accessToken);
+  const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
+  const gitlab = await _authenticate(refreshedCopilot.accessToken);
   await _removeAssignees(gitlab, projectId, issueId, [userId]);
   logger.debug(`Gitlab user ${userId} is unassigned from issue number ${issueId}`);
 }
@@ -179,7 +192,8 @@ removeAssign.schema = assignUser.schema;
  */
 async function getUsernameById(copilot, userId) {
   Joi.attempt({copilot, userId}, getUsernameById.schema);
-  const gitlab = await _authenticate(copilot.accessToken);
+  const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
+  const gitlab = await _authenticate(refreshedCopilot.accessToken);
   const user = await gitlab.users.show(userId);
   return user ? user.username : null;
 }
@@ -197,7 +211,8 @@ getUsernameById.schema = {
  */
 async function getUserIdByLogin(copilot, login) {
   Joi.attempt({copilot, login}, getUserIdByLogin.schema);
-  const gitlab = await _authenticate(copilot.accessToken);
+  const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
+  const gitlab = await _authenticate(refreshedCopilot.accessToken);
   const user = await gitlab.users.all({username: login});
   return user.length ? user[0].id : null;
 }
@@ -220,7 +235,8 @@ getUserIdByLogin.schema = {
 async function markIssueAsPaid(copilot, project, issueId, challengeUUID, existLabels, winner, createCopilotPayments) { // eslint-disable-line max-params
   const projectId = project.id;
   Joi.attempt({copilot, projectId, issueId, challengeUUID, existLabels, winner, createCopilotPayments}, markIssueAsPaid.schema);
-  const gitlab = await _authenticate(copilot.accessToken);
+  const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
+  const gitlab = await _authenticate(refreshedCopilot.accessToken);
   const labels = _(existLabels).filter((i) => i !== config.FIX_ACCEPTED_ISSUE_LABEL)
     .push(config.FIX_ACCEPTED_ISSUE_LABEL, config.PAID_ISSUE_LABEL).value();
   try {
@@ -263,7 +279,8 @@ markIssueAsPaid.schema = {
 async function changeState(copilot, project, issueId, state) {
   const projectId = project.id;
   Joi.attempt({copilot, projectId, issueId, state}, changeState.schema);
-  const gitlab = await _authenticate(copilot.accessToken);
+  const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
+  const gitlab = await _authenticate(refreshedCopilot.accessToken);
   try {
     await gitlab.projects.issues.edit(projectId, issueId, {state_event: state});
   } catch (err) {
@@ -289,7 +306,8 @@ changeState.schema = {
 async function addLabels(copilot, project, issueId, labels) {
   const projectId = project.id;
   Joi.attempt({copilot, projectId, issueId, labels}, addLabels.schema);
-  const gitlab = await _authenticate(copilot.accessToken);
+  const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
+  const gitlab = await _authenticate(refreshedCopilot.accessToken);
   try {
     await gitlab.projects.issues.edit(projectId, issueId, {labels: _.join(labels, ',')});
   } catch (err) {
@@ -305,6 +323,35 @@ addLabels.schema = {
   labels: Joi.array().items(Joi.string()).required()
 };
 
+/**
+ * Refresh the copilot access token if token is needed
+ * @param {Object} copilot the copilot
+ * @returns {Promise} the promise result of copilot with refreshed token
+ */
+async function _refreshGitlabUserAccessToken(copilot) {
+  if (copilot.accessTokenExpiration && new Date().getTime() > copilot.accessTokenExpiration.getTime() -
+    (config.GITLAB_REFRESH_TOKEN_BEFORE_EXPIRATION * MS_PER_SECOND)) {
+    const refreshTokenResult = await request
+      .post(`${config.GITLAB_API_BASE_URL}/oauth/token`)
+      .query({
+        client_id: config.GITLAB_CLIENT_ID,
+        client_secret: config.GITLAB_CLIENT_SECRET,
+        refresh_token: copilot.refreshToken,
+        grant_type: 'refresh_token',
+        redirect_uri: config.GITLAB_OWNER_USER_CALLBACK_URL,
+      })
+      .end();
+      // save user token data
+    const expiresIn = refreshTokenResult.body.expires_in || config.GITLAB_ACCESS_TOKEN_DEFAULT_EXPIRATION;
+    return await dbHelper.update(User, copilot.id, {
+      accessToken: refreshTokenResult.body.access_token,
+      accessTokenExpiration: new Date(new Date().getTime() + expiresIn * MS_PER_SECOND),
+      refreshToken: refreshTokenResult.body.refresh_token,
+    });
+  }
+  return copilot;
+}
+
 
 module.exports = {
   createComment,
diff --git a/services/UserService.js b/services/UserService.js
index 3b1b342..71379e7 100755
--- a/services/UserService.js
+++ b/services/UserService.js
@@ -97,6 +97,8 @@ async function getRepositoryCopilotOrOwner(provider, repoFullName) {
 
   return {
     accessToken: user.accessToken,
+    accessTokenExpiration: user.accessTokenExpiration,
+    refreshToken: user.refreshToken,
     userProviderId: user.userProviderId,
     topcoderUsername: userMapping.topcoderUsername
   };