Skip to content

How to Create a User in Oracle - Grants & Privileges

Creating a user in Oracle involves more than just CREATE USER — you need to assign a default tablespace, set a quota, grant the right privileges, and attach a profile for password and resource management. This guide covers every step for a production-ready user.

You need the CREATE USER system privilege (typically held by DBAs). For multi-tenant databases (CDB/PDB), decide whether you’re creating a common user (C## prefix, exists in all containers) or a local user (PDB-only).

-- Check your current privileges
SELECT privilege FROM session_privs WHERE privilege = 'CREATE USER';
-- If working in a CDB, check which container you're in
SHOW CON_NAME;
-- For PDB work, connect to the right PDB
ALTER SESSION SET CONTAINER = pdb_prod;
-- Create a standard application user
CREATE USER app_user
IDENTIFIED BY "SecureP@ssw0rd2024"
DEFAULT TABLESPACE app_data
TEMPORARY TABLESPACE temp
QUOTA 10G ON app_data
ACCOUNT UNLOCK;
-- Grant CREATE SESSION so the user can log in
GRANT CREATE SESSION TO app_user;

Without GRANT CREATE SESSION, the user will receive ORA-01045 when trying to connect.

Oracle provides predefined roles that bundle common privileges.

-- For a developer/application schema owner
GRANT CONNECT, RESOURCE TO app_user;
-- RESOURCE includes: CREATE TABLE, CREATE INDEX, CREATE PROCEDURE,
-- CREATE SEQUENCE, CREATE TRIGGER, CREATE TYPE, CREATE CLUSTER
-- For a read-only reporting user
GRANT CREATE SESSION TO report_user;
GRANT SELECT ANY TABLE TO report_user; -- Broad — prefer object-level grants
-- For a DBA user (admin work only, not application connections)
GRANT DBA TO dba_app_user;

Prefer explicit object grants over broad system privileges — this follows the principle of least privilege.

-- Grant SELECT on specific tables to a reporting user
GRANT SELECT ON app_schema.orders TO report_user;
GRANT SELECT ON app_schema.customers TO report_user;
GRANT SELECT ON app_schema.products TO report_user;
-- Grant DML permissions for an application user
GRANT SELECT, INSERT, UPDATE, DELETE ON app_schema.orders TO app_user;
GRANT SELECT, INSERT ON app_schema.audit_log TO app_user;
-- Grant EXECUTE on packages and procedures
GRANT EXECUTE ON app_schema.order_pkg TO app_user;
GRANT EXECUTE ON app_schema.util_pkg TO app_user;
-- Allow the user to grant their own privileges to others (with GRANT OPTION)
GRANT SELECT ON app_schema.reference_data TO report_user WITH GRANT OPTION;

Without a quota, a user with RESOURCE role or UNLIMITED TABLESPACE will consume space without limits.

-- Set a specific quota
ALTER USER app_user QUOTA 20G ON app_data;
ALTER USER app_user QUOTA 2G ON app_idx;
-- Remove quota restriction (unlimited on a specific tablespace)
ALTER USER app_user QUOTA UNLIMITED ON app_data;
-- Check current quotas
SELECT username, tablespace_name,
ROUND(bytes/1024/1024, 0) AS used_mb,
DECODE(max_bytes, -1, 'UNLIMITED',
ROUND(max_bytes/1024/1024, 0)) AS quota_mb
FROM dba_ts_quotas
WHERE username = 'APP_USER';

Note: GRANT RESOURCE in Oracle 12c+ no longer implicitly grants UNLIMITED TABLESPACE. You must set quotas explicitly.

Profiles enforce password policies and resource limits.

-- Create a profile for application users
CREATE PROFILE app_profile LIMIT
FAILED_LOGIN_ATTEMPTS 5
PASSWORD_LOCK_TIME 1/24 -- 1 hour
PASSWORD_LIFE_TIME 180 -- 180 days
PASSWORD_REUSE_TIME 365
PASSWORD_REUSE_MAX 10
PASSWORD_GRACE_TIME 7
PASSWORD_VERIFY_FUNCTION ora12c_strong_verify_function
SESSIONS_PER_USER UNLIMITED
CPU_PER_SESSION UNLIMITED
CONNECT_TIME UNLIMITED
IDLE_TIME 60; -- Disconnect after 60 min idle
-- Assign profile to user
ALTER USER app_user PROFILE app_profile;
-- Verify profile assignment
SELECT username, profile, account_status, expiry_date
FROM dba_users
WHERE username = 'APP_USER';

Step 6: Create a Schema-Only Account (Oracle 18c+)

Section titled “Step 6: Create a Schema-Only Account (Oracle 18c+)”

Schema-only accounts have no password and cannot log in directly — they just own objects. Applications connect as a different user and reference the schema.

-- Create schema-only account (no password, no login)
CREATE USER app_schema NO AUTHENTICATION
DEFAULT TABLESPACE app_data
QUOTA UNLIMITED ON app_data;
-- Create the connecting application user
CREATE USER app_connect
IDENTIFIED BY "ConnectP@ss2024"
DEFAULT TABLESPACE app_data
TEMPORARY TABLESPACE temp;
GRANT CREATE SESSION TO app_connect;
-- Grant app_connect access to app_schema objects
GRANT SELECT, INSERT, UPDATE, DELETE
ON app_schema.orders TO app_connect;
-- Must be connected as a common user with CREATE USER privilege
-- Common users must start with C##
CREATE USER c##dba_monitor
IDENTIFIED BY "MonitorP@ss2024"
CONTAINER = ALL;
GRANT SET CONTAINER TO c##dba_monitor CONTAINER=ALL;
GRANT CREATE SESSION TO c##dba_monitor CONTAINER=ALL;
GRANT SELECT ANY DICTIONARY TO c##dba_monitor CONTAINER=ALL;

Proxy authentication lets one user connect on behalf of another — useful for connection pools.

-- Allow app_pool to proxy for app_user
ALTER USER app_user GRANT CONNECT THROUGH app_pool;
-- Application connects as: app_pool[app_user]
-- Verify proxy configuration
SELECT proxy, client, authentication, flags
FROM proxy_users
WHERE client = 'APP_USER';

No DEFAULT TABLESPACE specified — The user lands in the SYSTEM tablespace by default, and any objects they create pollute the SYSTEM tablespace.

Granting DBA to application usersGRANT DBA is far too broad for application accounts. It grants access to every object and every system privilege.

Forgetting to set a quota — A user without a quota or UNLIMITED TABLESPACE privilege gets ORA-01950 when trying to create objects.

Weak passwords — Oracle 12c+ password complexity functions are available. Use them via profiles to enforce strength requirements.

Not assigning a profile — Without a profile, DEFAULT is used, which has unlimited failed login attempts and no password expiry in some configurations.

Creating users as SYS or SYSTEM — Never create application users that connect as SYS or SYSTEM. Create a dedicated DBA account instead.

-- Confirm user was created correctly
SELECT
username,
account_status,
default_tablespace,
temporary_tablespace,
profile,
created,
expiry_date
FROM dba_users
WHERE username = 'APP_USER';
-- Check all granted system privileges
SELECT grantee, privilege, admin_option
FROM dba_sys_privs
WHERE grantee = 'APP_USER'
ORDER BY privilege;
-- Check all granted roles
SELECT grantee, granted_role, admin_option, default_role
FROM dba_role_privs
WHERE grantee = 'APP_USER'
ORDER BY granted_role;
-- Check tablespace quotas
SELECT tablespace_name,
ROUND(bytes/1024/1024, 0) AS used_mb,
DECODE(max_bytes, -1, 'UNLIMITED',
ROUND(max_bytes/1024/1024, 0)) AS limit_mb
FROM dba_ts_quotas
WHERE username = 'APP_USER';
-- Check object-level grants the user has received
SELECT owner, table_name, privilege, grantable
FROM dba_tab_privs
WHERE grantee = 'APP_USER'
ORDER BY owner, table_name;