diff --git a/hiboo/sso/oidc.py b/hiboo/sso/oidc.py
index 86e6557b61e7bdd8f3507a6750ecb894abf354ad..eb7f944b3b8be3d63d66fa2515dae62adbcd3338 100644
--- a/hiboo/sso/oidc.py
+++ b/hiboo/sso/oidc.py
@@ -10,10 +10,6 @@ from hiboo import models, utils, identity
 import flask
 
 
-AUTHORIZATION_CODES = {}
-NONCES = {}
-
-
 class Config(object):
     """ Handles service configuration and forms.
     """
@@ -90,27 +86,26 @@ class AuthorizationCodeGrant(grants_oauth2.AuthorizationCodeGrant):
 
     def create_authorization_code(self, client, grant_user, request):
         code = gen_salt(48)  # TODO
-        nonce = request.data.get('nonce')
-        item = AuthorizationCode(
-            code=code,
+        authorization_code = AuthorizationCode(
+            code=code, nonce=request.data.get('nonce'),
             client_id=client.client_id,
             redirect_uri=request.redirect_uri,
             scope=request.scope,
-            user_id=grant_user.uuid,
-            nonce=nonce
+            user_id=grant_user.uuid
         )
-        AUTHORIZATION_CODES[code] = item
-        NONCES[nonce] = item
+        models.db.session.add(authorization_code)
+        models.db.session.commit()
         return code
 
     def parse_authorization_code(self, code, client):
-        code = AUTHORIZATION_CODES.get(code)
-        if code and code.client_id == client.client_id:
-            return code
+        return AuthorizationCode.query.filter_by(
+            client_id=client.client_id,
+            code=code
+        ).first()
 
     def delete_authorization_code(self, authorization_code):
-        del AUTHORIZATION_CODES[authorization_code.code]
-        del NONCES[authorization_code.nonce]
+        models.db.session.delete(authorization_code)
+        models.db.session.commit()
 
     def authenticate_user(self, authorization_code):
         profile = models.Identity.query.get(authorization_code.user_id)
@@ -120,6 +115,7 @@ class AuthorizationCodeGrant(grants_oauth2.AuthorizationCodeGrant):
 class AuthorizationCode(models.db.Model, models_oauth2.OIDCAuthorizationCodeMixin):
     """ Authorization code object for storage
     """
+    __tablename__ = "oidc_authorization_code"
 
     user_id = models.db.Column(models.db.Text())
 
@@ -129,8 +125,9 @@ class OpenIDCode(oidc.grants.OpenIDCode):
     """
 
     def exists_nonce(self, nonce, request):
-        nonce = NONCES.get(nonce)
-        return nonce and nonce["client_id"] == request.client_id
+        return bool(AuthorizationCode.query.filter_by(
+            nonce=nonce, client_id=request.client_id).first()
+        )
 
     def get_jwt_config(self, grant):
         return {  # TODO
diff --git a/migrations/versions/5271f611b98b_.py b/migrations/versions/5271f611b98b_.py
new file mode 100644
index 0000000000000000000000000000000000000000..2afd9143fb0f449c2e3f2d58b8d52369911b0838
--- /dev/null
+++ b/migrations/versions/5271f611b98b_.py
@@ -0,0 +1,36 @@
+""" Add authorization codes in the database
+
+Revision ID: 5271f611b98b
+Revises: cfb466a78348
+Create Date: 2019-11-03 17:57:55.989647
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+revision = '5271f611b98b'
+down_revision = 'cfb466a78348'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    op.create_table('oidc_authorization_code',
+    sa.Column('code', sa.String(length=120), nullable=False),
+    sa.Column('client_id', sa.String(length=48), nullable=True),
+    sa.Column('redirect_uri', sa.Text(), nullable=True),
+    sa.Column('response_type', sa.Text(), nullable=True),
+    sa.Column('scope', sa.Text(), nullable=True),
+    sa.Column('auth_time', sa.Integer(), nullable=False),
+    sa.Column('nonce', sa.Text(), nullable=True),
+    sa.Column('user_id', sa.Text(), nullable=True),
+    sa.Column('uuid', sa.String(length=36), nullable=False),
+    sa.Column('created_at', sa.DateTime(), nullable=False),
+    sa.Column('updated_at', sa.DateTime(), nullable=True),
+    sa.Column('comment', sa.String(length=255), nullable=True),
+    sa.PrimaryKeyConstraint('uuid'),
+    sa.UniqueConstraint('code')
+    )
+
+def downgrade():
+    op.drop_table('oidc_authorization_code')