diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,10 @@
 
 ## Unreleased
 
+## 0.6.0.0
+
+Depend on `validation-micro` rather than `validation-selective`.
+
 ## 0.5.0.0
 
 Add 'getE', 'postE', 'tryReq' to pattern match against HTTP exceptions
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -7,16 +7,25 @@
 ![main](https://github.com/unfoldml/ms-graph-api/actions/workflows/haskell.yml/badge.svg?branch=main)
 
 
-## Description
+## Introduction
 
-This library provides both the client interface and authorization infrastructure to integrate with Microsoft infrastructure, e.g. using ActiveDirectory as an OAuth2 identity provider.
+This library provides both the client interface (under the `MSGraphAPI` namespace) as well as helpers to integrate with Microsoft infrastructure, e.g. using ActiveDirectory as an OAuth2 identity provider.
 
+With the provided auth functions (see `Network.OAuth2.Session`) you can easily implement OAuth2 for your application, and the resulting token store is thread-safe and automatically renews before expiring.
+
 ## Status
 
-This library is still in development, so expect missing funcionality.
+This library is still in development, so expect missing functionality.
 If there's anything you would like to see added, feel free to
 [open an issue](https://github.com/unfoldml/ms-graph-api/issues/new).
+In general, since the MS Graph API is quite large, features will be added to this library on a need basis.
+
+## Evolution of the library
+
 Some breaking changes might also be introduced as the library matures.
+
+We adhere to a simplified version of the [Package Versioning Policy](https://pvp.haskell.org/): breaking changes are signaled by increasing the major version number (e.g. 0.x -> 1.x ).
+
 
 ## Copyright
 
diff --git a/ms-graph-api.cabal b/ms-graph-api.cabal
--- a/ms-graph-api.cabal
+++ b/ms-graph-api.cabal
@@ -1,5 +1,5 @@
 name:                ms-graph-api
-version:             0.5.0.0
+version:             0.6.0.0
 synopsis:            Microsoft Graph API
 description:         Bindings to the Microsoft Graph API
 homepage:            https://github.com/unfoldml/ms-graph-api
@@ -20,8 +20,10 @@
   hs-source-dirs:      src
   exposed-modules:     
                        MSGraphAPI.User
+                       MSGraphAPI.Users.User
+                       MSGraphAPI.Users.Group
                        MSGraphAPI.Drive
-                       MSGraphAPI.Files.DriveItems
+                       MSGraphAPI.Files.DriveItem
                        Network.OAuth2.Provider.AzureAD
                        Network.OAuth2.Session
                        MSGraphAPI.Internal.Common
@@ -45,7 +47,7 @@
                      , transformers >= 0.5
                      , unliftio
                      , uri-bytestring
-                     , validation-selective
+                     , validation-micro
                      , wai
                      , warp
   ghc-options:         -Wall
diff --git a/src/MSGraphAPI/Files/DriveItem.hs b/src/MSGraphAPI/Files/DriveItem.hs
new file mode 100644
--- /dev/null
+++ b/src/MSGraphAPI/Files/DriveItem.hs
@@ -0,0 +1,46 @@
+module MSGraphAPI.Files.DriveItem where
+
+import GHC.Generics (Generic(..))
+
+-- aeson
+import qualified Data.Aeson as A (ToJSON(..), FromJSON(..), genericParseJSON)
+-- bytestring
+import qualified Data.ByteString.Lazy as LBS (ByteString)
+-- hoauth
+import Network.OAuth.OAuth2.Internal (AccessToken(..))
+-- req
+import Network.HTTP.Req (Req)
+-- text
+import Data.Text (Text, pack, unpack)
+-- time
+import Data.Time (LocalTime)
+
+import qualified MSGraphAPI.Internal.Common as MSG (get, getLbs, post, Collection, aesonOptions)
+
+data DriveItem = DriveItem {
+  diId :: Text
+  , diName :: Text
+  , diLastModifiedDateTime :: LocalTime
+                           } deriving (Eq, Ord, Show, Generic)
+instance A.FromJSON DriveItem where
+  parseJSON = A.genericParseJSON (MSG.aesonOptions "di")
+
+
+-- | download a complete file from user's directory
+--
+-- @GET \/me\/drive\/items\/{item-id}\/content@
+--
+-- https://learn.microsoft.com/en-us/graph/api/driveitem-get-content?view=graph-rest-1.0&tabs=http#request
+downloadFileMe :: Text -- ^ item ID
+               -> AccessToken -> Req LBS.ByteString
+downloadFileMe itemId = MSG.getLbs ["me", "drive", "items", itemId, "content"] mempty
+
+-- | download a file from a drive
+--
+-- @GET \/drives\/{drive-id}\/items\/{item-id}\/content@
+--
+-- https://learn.microsoft.com/en-us/graph/api/driveitem-get-content?view=graph-rest-1.0&tabs=http#request
+downloadFile :: Text -- ^ drive ID
+             -> Text -- ^ file ID
+             -> AccessToken -> Req LBS.ByteString
+downloadFile did itemId = MSG.getLbs ["drives", did, "items", itemId, "content"] mempty
diff --git a/src/MSGraphAPI/Files/DriveItems.hs b/src/MSGraphAPI/Files/DriveItems.hs
deleted file mode 100644
--- a/src/MSGraphAPI/Files/DriveItems.hs
+++ /dev/null
@@ -1,35 +0,0 @@
-module MSGraphAPI.Files.DriveItems where
-
-import GHC.Generics (Generic(..))
-
--- aeson
-import qualified Data.Aeson as A (ToJSON(..), FromJSON(..), genericParseJSON)
--- bytestring
-import qualified Data.ByteString.Lazy as LBS (ByteString)
--- hoauth
-import Network.OAuth.OAuth2.Internal (AccessToken(..))
--- req
-import Network.HTTP.Req (Req)
--- text
-import Data.Text (Text, pack, unpack)
-
-import qualified MSGraphAPI.Internal.Common as MSG (get, getLbs, post, Collection, aesonOptions)
-
--- | download a complete file from user's directory
---
--- @GET \/me\/drive\/items\/{item-id}\/content@
---
--- https://learn.microsoft.com/en-us/graph/api/driveitem-get-content?view=graph-rest-1.0&tabs=http#request
-downloadFileMe :: Text -- ^ item ID
-               -> AccessToken -> Req LBS.ByteString
-downloadFileMe itemId = MSG.getLbs ["me", "drive", "items", itemId, "content"] mempty
-
--- | download a file from a drive
---
--- @GET \/drives\/{drive-id}\/items\/{item-id}\/content@
---
--- https://learn.microsoft.com/en-us/graph/api/driveitem-get-content?view=graph-rest-1.0&tabs=http#request
-downloadFile :: Text -- ^ drive ID
-             -> Text -- ^ file ID
-             -> AccessToken -> Req LBS.ByteString
-downloadFile did itemId = MSG.getLbs ["drives", did, "items", itemId, "content"] mempty
diff --git a/src/MSGraphAPI/User.hs b/src/MSGraphAPI/User.hs
--- a/src/MSGraphAPI/User.hs
+++ b/src/MSGraphAPI/User.hs
@@ -1,57 +1,35 @@
 module MSGraphAPI.User where
 
-import GHC.Generics (Generic(..))
 
-import Data.List (sort, sortBy, stripPrefix, uncons)
-import Data.Maybe (listToMaybe, fromMaybe)
--- import Data.Ord (comparing)
-import Data.Char (toLower)
-import Data.String (IsString(..))
-import Data.Word (Word)
 
--- aeson
-import qualified Data.Aeson as A (ToJSON(..), FromJSON(..), genericParseJSON, defaultOptions, Options(..), withObject, withText, (.:), (.:?), object, (.=), Key, Value, camelTo2)
-import qualified Data.Aeson.Types as A (Parser, Object)
--- import qualified Data.Aeson.KeyMap as AKV (KeyMap, lookup)
--- containers
--- import qualified Data.Map as M (Map, empty, insert, lookup)
--- hoauth
-import Network.OAuth.OAuth2.Internal (AccessToken(..))
--- req
-import Network.HTTP.Req (Req)
--- text
-import Data.Text (Text, pack, unpack)
--- time
-import Data.Time.LocalTime (ZonedTime, zonedTimeToLocalTime)
+-- import Data.List (sort, sortBy, stripPrefix, uncons)
+-- import Data.Maybe (listToMaybe, fromMaybe)
+-- -- import Data.Ord (comparing)
+-- import Data.Char (toLower)
+-- import Data.String (IsString(..))
+-- import Data.Word (Word)
 
-import qualified MSGraphAPI.Internal.Common as MSG (get, post, aesonOptions)
+-- -- aeson
+-- import qualified Data.Aeson as A (ToJSON(..), FromJSON(..), genericParseJSON, defaultOptions, Options(..), withObject, withText, (.:), (.:?), object, (.=), Key, Value, camelTo2)
+-- import qualified Data.Aeson.Types as A (Parser, Object)
+-- -- import qualified Data.Aeson.KeyMap as AKV (KeyMap, lookup)
+-- -- containers
+-- -- import qualified Data.Map as M (Map, empty, insert, lookup)
+-- -- hoauth
+-- import Network.OAuth.OAuth2.Internal (AccessToken(..))
+-- -- req
+-- import Network.HTTP.Req (Req)
+-- -- text
+-- import Data.Text (Text, pack, unpack)
+-- -- time
+-- import Data.Time.LocalTime (ZonedTime, zonedTimeToLocalTime)
 
--- | Get user information
---
--- @GET \/users\/{user-id}@
---
--- https://learn.microsoft.com/en-us/graph/api/user-get?view=graph-rest-1.0&tabs=http#request
-get :: Text -- ^ user id
-    -> AccessToken -> Req User
-get uid = MSG.get ["users", uid] mempty
+-- import qualified MSGraphAPI.Internal.Common as MSG (get, post, aesonOptions)
+-- -- import MSGraphAPI.Users.User (User)
 
--- | Get information on signed-in user
---
--- Calling the \/me endpoint requires a signed-in user and therefore a delegated permission. Application permissions are not supported when using the \/me endpoint.
---
--- @GET \/me@
---
--- https://learn.microsoft.com/en-us/graph/api/user-get?view=graph-rest-1.0&tabs=http#request-1
-getMe :: AccessToken -> Req User
-getMe = MSG.get ["me"] mempty
 
 
-data User = User {
-  uId :: Text
-  , uUserPrincipalName :: Text
-  , uDisplayName :: Text
-                 } deriving (Eq, Ord, Show, Generic)
-instance A.FromJSON User where
-  parseJSON = A.genericParseJSON (MSG.aesonOptions "u")
+
+
 
 
diff --git a/src/MSGraphAPI/Users/Group.hs b/src/MSGraphAPI/Users/Group.hs
new file mode 100644
--- /dev/null
+++ b/src/MSGraphAPI/Users/Group.hs
@@ -0,0 +1,55 @@
+-- | User
+module MSGraphAPI.Users.Group where
+
+import GHC.Generics (Generic(..))
+
+-- aeson
+import qualified Data.Aeson as A (ToJSON(..), FromJSON(..), eitherDecode, genericParseJSON, defaultOptions, Options(..), withObject, withText, (.:), (.:?), object, (.=))
+-- hoauth
+import Network.OAuth.OAuth2.Internal (AccessToken(..))
+-- req
+import Network.HTTP.Req (Req)
+-- text
+import Data.Text (Text, pack, unpack)
+
+import qualified MSGraphAPI.Internal.Common as MSG (Collection(..), get, post, aesonOptions)
+import MSGraphAPI.Files.DriveItem (DriveItem)
+
+-- | Groups are collections of principals with shared access to resources in Microsoft services or in your app. Different principals such as users, other groups, devices, and applications can be part of groups. 
+--
+-- httpstea://learn.microsoft.com/en-us/graph/api/resources/groups-overview?view=graph-rest-1.0&tabs=http
+data Group = Group {
+  gId :: Text
+  , gDisplayName :: Text
+  , gDescription :: Text
+                   } deriving (Eq, Ord, Show, Generic)
+instance A.FromJSON Group where
+  parseJSON = A.genericParseJSON (MSG.aesonOptions "g")
+
+-- | Get the teams in Microsoft Teams that the user is a direct member of.
+--
+-- @GET \/users\/{id | user-principal-name}\/joinedTeams@
+--
+-- https://learn.microsoft.com/en-us/graph/api/user-list-joinedteams?view=graph-rest-1.0&tabs=http
+getUserJoinedTeams :: Text -- ^ User ID
+                   -> AccessToken -> Req (MSG.Collection Group)
+getUserJoinedTeams uid = MSG.get ["users", uid, "joinedTeams"] mempty
+
+-- | Get the 'DriveItem's in the 'Group' storage, starting from the root item
+--
+-- @GET \/groups\/{group-id}\/drive\/root\/children@
+--
+-- https://learn.microsoft.com/en-us/graph/api/driveitem-list-children?view=graph-rest-1.0&tabs=http
+getGroupsDriveItems :: Text -- ^ Group ID
+                    -> AccessToken -> Req (MSG.Collection DriveItem)
+getGroupsDriveItems gid = MSG.get ["groups", gid, "drive", "root", "children"] mempty
+
+
+-- data X = X { xName :: Text } deriving (Eq, Ord, Show, Generic)
+-- instance A.FromJSON X where
+--   parseJSON = A.genericParseJSON (MSG.aesonOptions "x")
+
+-- pt0 :: Either String (MSG.Collection DriveItem)
+-- pt0 = A.eitherDecode t0
+--   where
+--     t0 = "{\r\n  \"value\": [\r\n    {\"name\": \"myfile.jpg\"  },\r\n    {\"name\": \"Documents\" },\r\n    {\"name\": \"Photos\" },\r\n    {\"name\": \"my sheet(1).xlsx\"}\r\n  ]\r\n}"
diff --git a/src/MSGraphAPI/Users/User.hs b/src/MSGraphAPI/Users/User.hs
new file mode 100644
--- /dev/null
+++ b/src/MSGraphAPI/Users/User.hs
@@ -0,0 +1,44 @@
+-- | User
+module MSGraphAPI.Users.User where
+
+import GHC.Generics (Generic(..))
+
+-- aeson
+import qualified Data.Aeson as A (ToJSON(..), FromJSON(..), genericParseJSON, defaultOptions, Options(..), withObject, withText, (.:), (.:?), object, (.=))
+-- hoauth
+import Network.OAuth.OAuth2.Internal (AccessToken(..))
+-- req
+import Network.HTTP.Req (Req)
+-- text
+import Data.Text (Text, pack, unpack)
+
+import qualified MSGraphAPI.Internal.Common as MSG (get, post, aesonOptions)
+
+
+data User = User {
+  uId :: Text
+  , uUserPrincipalName :: Text
+  , uDisplayName :: Text
+                 } deriving (Eq, Ord, Show, Generic)
+instance A.FromJSON User where
+  parseJSON = A.genericParseJSON (MSG.aesonOptions "u")
+
+
+-- | Get user information
+--
+-- @GET \/users\/{user-id}@
+--
+-- https://learn.microsoft.com/en-us/graph/api/user-get?view=graph-rest-1.0&tabs=http#request
+get :: Text -- ^ user id
+    -> AccessToken -> Req User
+get uid = MSG.get ["users", uid] mempty
+
+-- | Get information on signed-in user
+--
+-- Calling the \/me endpoint requires a signed-in user and therefore a delegated permission. Application permissions are not supported when using the \/me endpoint.
+--
+-- @GET \/me@
+--
+-- https://learn.microsoft.com/en-us/graph/api/user-get?view=graph-rest-1.0&tabs=http#request-1
+getMe :: AccessToken -> Req User
+getMe = MSG.get ["me"] mempty
diff --git a/src/Network/OAuth2/JWT.hs b/src/Network/OAuth2/JWT.hs
--- a/src/Network/OAuth2/JWT.hs
+++ b/src/Network/OAuth2/JWT.hs
@@ -26,8 +26,8 @@
 import qualified Data.Text as T (Text, unpack)
 -- time
 import Data.Time (UTCTime(..), NominalDiffTime, getCurrentTime, fromGregorian, addUTCTime, diffUTCTime)
--- validation-selective
-import Validation (Validation(..), failure, validationToEither, maybeToSuccess)
+-- validation-micro
+import Validation.Micro (Validation(..), failure, validationToEither, maybeToSuccess)
 
 
 -- | 'sub' field
diff --git a/src/Network/OAuth2/Provider/AzureAD.hs b/src/Network/OAuth2/Provider/AzureAD.hs
--- a/src/Network/OAuth2/Provider/AzureAD.hs
+++ b/src/Network/OAuth2/Provider/AzureAD.hs
@@ -45,6 +45,8 @@
                          }
 
 -- | NB : scopes @openid@ and @offline_access@ are ALWAYS requested since the library assumes we have access to refresh tokens and ID tokens
+--
+-- Reference on Microsoft Graph permissions : https://learn.microsoft.com/en-us/graph/permissions-reference
 azureADApp :: OAuthCfg -- ^ OAuth configuration
            -> IdpApplication 'AuthorizationCode AzureAD
 azureADApp (OAuthCfg appname clid sec scopes authstate reduri) = defaultAzureADApp{
diff --git a/src/Network/OAuth2/Session.hs b/src/Network/OAuth2/Session.hs
--- a/src/Network/OAuth2/Session.hs
+++ b/src/Network/OAuth2/Session.hs
@@ -65,7 +65,7 @@
 -- uri-bytestring
 import URI.ByteString (URI)
 -- validation-selective
-import Validation (Validation, failure, validationToEither)
+import Validation.Micro (Validation, failure, validationToEither)
 
 import Network.OAuth2.Provider.AzureAD (OAuthCfg, azureADApp, AzureAD)
 import Network.OAuth2.JWT (jwtClaims, UserSub(..), userSub, ApiAudience, apiAudience, decValidSub, decValidExp, decValidNbf, JWTException(..))
