diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,5 @@
+# Changelog for `password-aeson`
+
+## 0.1.0.0
+
+- Split from `password-instances`.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) Dennis Gosnell, 2019; Felix Paulusma, 2020
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Dennis Gosnell, Felix Paulusma nor the names
+      of other contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,11 @@
+# password-aeson
+
+[![Build Status](https://github.com/cdepillabout/password/workflows/password/badge.svg)](http://github.com/cdepillabout/password)
+[![Hackage](https://img.shields.io/hackage/v/password-aeson.svg)](https://hackage.haskell.org/package/password-aeson)
+[![Stackage LTS](http://stackage.org/package/password-aeson/badge/lts)](http://stackage.org/lts/package/password-aeson)
+[![Stackage Nightly](http://stackage.org/package/password-aeson/badge/nightly)](http://stackage.org/nightly/package/password-aeson)
+[![BSD3 license](https://img.shields.io/badge/license-BSD3-blue.svg)](./LICENSE)
+
+This package provides `aeson` typeclass instances for the plain-text password
+and hashed password datatypes from the
+[password](https://hackage.haskell.org/package/password) package.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,33 @@
+{-# LANGUAGE CPP #-}
+{-# OPTIONS_GHC -Wall #-}
+module Main (main) where
+
+#ifndef MIN_VERSION_cabal_doctest
+#define MIN_VERSION_cabal_doctest(x,y,z) 0
+#endif
+
+#if MIN_VERSION_cabal_doctest(1,0,0)
+
+import Distribution.Extra.Doctest ( defaultMainWithDoctests )
+main :: IO ()
+main = defaultMainWithDoctests "doctests"
+
+#else
+
+#ifdef MIN_VERSION_Cabal
+-- If the macro is defined, we have new cabal-install,
+-- but for some reason we don't have cabal-doctest in package-db
+--
+-- Probably we are running cabal sdist, when otherwise using new-build
+-- workflow
+#warning You are configuring this package without cabal-doctest installed. \
+         The doctests test-suite will not work as a result. \
+         To fix this, install cabal-doctest before configuring.
+#endif
+
+import Distribution.Simple
+
+main :: IO ()
+main = defaultMain
+
+#endif
diff --git a/password-aeson.cabal b/password-aeson.cabal
new file mode 100644
--- /dev/null
+++ b/password-aeson.cabal
@@ -0,0 +1,82 @@
+cabal-version: 1.12
+
+name:           password-aeson
+version:        0.1.0.0
+category:       Security
+synopsis:       aeson typeclass instances for password package
+description:    A library providing typeclass instances for aeson for the types from the password package.
+homepage:       https://github.com/cdepillabout/password/tree/master/password-aeson#readme
+bug-reports:    https://github.com/cdepillabout/password/issues
+author:         Dennis Gosnell, Felix Paulusma
+maintainer:     cdep.illabout@gmail.com, felix.paulusma@gmail.com
+copyright:      Copyright (c) Dennis Gosnell, 2019; Felix Paulusma, 2020
+license:        BSD3
+license-file:   LICENSE
+build-type:     Custom
+extra-source-files:
+    README.md
+    ChangeLog.md
+
+source-repository head
+  type: git
+  location: https://github.com/cdepillabout/password
+
+custom-setup
+  setup-depends:
+      base           >= 4.9  && < 5
+    , Cabal                     < 4
+    , cabal-doctest  >=1.0.6 && <1.1
+
+library
+  hs-source-dirs:
+      src
+  exposed-modules:
+      Data.Password.Aeson
+--   other-modules:
+--       Paths_password_aeson
+  build-depends:
+      base          >= 4.9 && < 5
+    , aeson         >= 0.2 && < 3
+    , password-types          < 2
+  ghc-options:
+      -Wall
+  default-language:
+      Haskell2010
+
+test-suite doctests
+  type:
+      exitcode-stdio-1.0
+  hs-source-dirs:
+      test/doctest
+  main-is:
+      doctest.hs
+  ghc-options:
+      -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      base >=4.9 && <5
+    , base-compat
+    , doctest
+    , password
+  default-language:
+      Haskell2010
+
+test-suite password-aeson-tasty
+  type:
+      exitcode-stdio-1.0
+  hs-source-dirs:
+      test/tasty
+  main-is:
+      Spec.hs
+  ghc-options:
+      -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      base >=4.9 && <5
+    , password-aeson
+    , password-types
+    , aeson
+    , quickcheck-instances
+    , tasty
+    , tasty-quickcheck
+    , text
+  default-language:
+      Haskell2010
diff --git a/src/Data/Password/Aeson.hs b/src/Data/Password/Aeson.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Password/Aeson.hs
@@ -0,0 +1,84 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE StandaloneDeriving #-}
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE UndecidableInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+{-|
+Module      : Data.Password.Aeson
+Copyright   : (c) Dennis Gosnell, 2019; Felix Paulusma, 2020
+License     : BSD-style (see LICENSE file)
+Maintainer  : cdep.illabout@gmail.com
+Stability   : experimental
+Portability : POSIX
+
+This module provides additional typeclass instances
+for 'Password' and 'PasswordHash', along with the
+'ExposedPassword' newtype if you /absolutely have to/
+convert a plain text password into JSON.
+
+See the "Data.Password.Types" module for more information.
+-}
+
+module Data.Password.Aeson (ExposedPassword (..)) where
+
+import Data.Aeson (FromJSON(..), ToJSON(..))
+import Data.Password.Types (
+  mkPassword,
+  unsafeShowPassword,
+  Password,
+  PasswordHash (PasswordHash),
+ )
+import GHC.TypeLits (TypeError, ErrorMessage(..))
+
+-- $setup
+-- >>> :set -XOverloadedStrings
+-- >>> :set -XDataKinds
+--
+-- Import needed functions.
+--
+-- >>> import Data.Aeson (decode)
+-- >>> import Data.Password.Bcrypt (Salt(..), hashPasswordWithSalt, unsafeShowPassword)
+
+-- | This instance allows a 'Password' to be created from a JSON blob.
+--
+-- >>> let maybePassword = decode "\"foobar\"" :: Maybe Password
+-- >>> fmap unsafeShowPassword maybePassword
+-- Just "foobar"
+--
+-- There is no instance for 'ToJSON' for 'Password' because we don't want to
+-- accidentally encode a plain-text 'Password' to JSON and send it to the end-user.
+--
+-- Similarly, there is no 'ToJSON' and 'FromJSON' instance for 'PasswordHash'
+-- because we don't want to accidentally send the password hash to the end
+-- user.
+instance FromJSON Password where
+  parseJSON = fmap mkPassword . parseJSON
+
+type ErrMsg = 'Text "Warning! Tried to convert plain-text Password to JSON!"
+         :$$: 'Text "  This is likely a security leak. Please make sure whether this was intended."
+         :$$: 'Text "  If this is intended, please use 'unsafeShowPassword' before converting to JSON"
+         :$$: 'Text ""
+
+-- | Type error! Do not use 'toJSON' on a 'Password'!
+instance TypeError ErrMsg => ToJSON Password where
+  toJSON = error "unreachable"
+
+-- | WARNING: DO NOT USE UNLESS ABSOLUTELY NECESSARY!
+--
+-- Using this newtype will allow your plain text password to be turned into
+-- JSON. Keep this type tightly bound to only the section where you want to
+-- expose the `Password`, since it's easy for a bigger type that contains
+-- this `ExposedPassword` to be logged or printed as JSON, and now you've
+-- accidentally leaked passwords in your logs or database.
+newtype ExposedPassword = ExposedPassword Password
+  deriving newtype (FromJSON)
+
+instance ToJSON ExposedPassword where
+  toJSON (ExposedPassword p) = toJSON $ unsafeShowPassword p
+
+deriving newtype instance FromJSON (PasswordHash a)
+
+deriving newtype instance ToJSON (PasswordHash a)
diff --git a/test/doctest/doctest.hs b/test/doctest/doctest.hs
new file mode 100644
--- /dev/null
+++ b/test/doctest/doctest.hs
@@ -0,0 +1,14 @@
+module Main where
+
+import Build_doctests (flags, pkgs, module_sources)
+-- import Data.Foldable (traverse_)
+import System.Environment.Compat (unsetEnv)
+import Test.DocTest (doctest)
+
+main :: IO ()
+main = do
+  -- traverse_ putStrLn args
+  unsetEnv "GHC_ENVIRONMENT"
+  doctest args
+  where
+    args = flags ++ pkgs ++ module_sources
diff --git a/test/tasty/Spec.hs b/test/tasty/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/tasty/Spec.hs
@@ -0,0 +1,38 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+import Data.Aeson
+import Data.Aeson.Types (parseMaybe)
+import Data.Text (Text)
+import Test.Tasty (TestTree, defaultMain, testGroup)
+import Test.Tasty.QuickCheck (testProperty, (===))
+import Test.QuickCheck.Instances.Text ()
+
+import Data.Password.Types (Password, unsafeShowPassword)
+import Data.Password.Aeson ()
+
+
+main :: IO ()
+main = defaultMain $ testGroup "Password Instances"
+  [ aesonTest
+  ]
+
+data TestUser = TestUser {
+  name :: Text,
+  password :: Password
+} deriving (Show)
+
+instance FromJSON TestUser where
+  parseJSON = withObject "TestUser" $ \o ->
+    TestUser <$> o .: "name" <*> o .: "password"
+
+aesonTest :: TestTree
+aesonTest =
+  testProperty "Password (Aeson)" $ \pwd ->
+    Just pwd === (unsafeShowPassword . password <$> parseIt pwd)
+  where
+    parseIt pwd =
+      parseMaybe parseJSON $
+        object
+          [ "name" .= String "testname"
+          , "password" .= String pwd
+          ]
